Wednesday, September 24, 2014

DryIoc ASP.NET Web API error

Error
Check your custom 'IHttpControllerActivator' and make sure that it will not manufacture the same instance.


Use Reuse.InResolutionScope instead:
using System;
using System.Linq;
using System.Web.Http;

using DryIoc;
using System.Reflection;

namespace Craft
{
    public class WebApiApplication : System.Web.HttpApplication
    {
        DryIoc.Container _container;

        protected void Application_Start()
        {
            GlobalConfiguration.Configure(WebApiConfig.Register);


            _container = new DryIoc.Container();                        

            Assembly assembly = typeof(WebApiApplication).Assembly;

            foreach (var controller in assembly.GetTypes().Where(t => typeof(ApiController).IsAssignableFrom(t)))
            {
                _container.Register(controller, DryIoc.Reuse.InResolutionScope);                
            }

            _container.Register<ISampleService, SampleService>(DryIoc.Reuse.Singleton);
            
            GlobalConfiguration.Configuration.Services.Replace(typeof(System.Web.Http.Dispatcher.IHttpControllerActivator), new ControllerActivator(_container));
        }

        public override void Dispose()
        {
            _container.Dispose();
            base.Dispose();
        }
    }



    // http://blog.ploeh.dk/2012/09/28/DependencyInjectionandLifetimeManagementwithASP.NETWebAPI/
    // http://blog.ploeh.dk/2012/10/03/DependencyInjectioninASP.NETWebAPIwithCastleWindsor/
    public class ControllerActivator : System.Web.Http.Dispatcher.IHttpControllerActivator
    {
        DryIoc.Container _container;

        public ControllerActivator(DryIoc.Container container)
        {
            _container = container;
        }

        public System.Web.Http.Controllers.IHttpController Create(
            System.Net.Http.HttpRequestMessage request,
            System.Web.Http.Controllers.HttpControllerDescriptor controllerDescriptor,
            Type controllerType)
        {

            var controller = (System.Web.Http.Controllers.IHttpController)_container.Resolve(controllerType);

            _container.ResolvePropertiesAndFields(controller);

            return controller;
        }       
    }

    public interface ISampleService
    {
        string GetMessage();
    }


    public class SampleService : ISampleService
    {

        string ISampleService.GetMessage()
        {
            return "Hello Kel " + Guid.NewGuid();
        }
    }

} // namespace



This code: _container.ResolvePropertiesAndFields(controller), is not needed when we pass the interfaces to constructor instead


P.S.

A good read on why ASP.NET Web API's IDependencyResolver is a Service Locator pattern:

"Jeff, thank you for writing. You are indeed correct that one of the many problems with the Service Locator anti-pattern(and therefore also IDependencyResolver) is that the overall context is hidden. Glenn Block originally pointed that particular problem out to me.
This is also the case with IDependencyResolver, because when GetService(Type) or GetServices(Type) is invoked, the only information the composition engine knows, is the requested type. Thus, resolving something that requires access to the HttpRequestMessage or one of its properties, is impossible with IDependencyResolver, but perfectly possible with IHttpControllerActivator.
So, yes, I would definitely say that any DI Container that provides 'ASP.NET Web API integration' by implementing IDependencyResolver is missing out. In any case, these days I rarely use a DI Container, so I don't consider it a big deal - and if I need to use a DI Container, I just need to add those few lines of code listed above in this blog post." -- Mark Seemann

And why Service Locator is an anti-pattern: http://blog.ploeh.dk/2010/02/03/ServiceLocatorisanAnti-Pattern/



Happy Coding!

No comments:

Post a Comment