How to use dependency injection with a controller

I have below code that will work without any problem MAUserController.cs

public class MAUserController : ApiController
    {
        ILogService loggerService;
        IMAUserService _service;

        public MAUserController(ILogService loggerService, IMAUserService Service)
        {
            this.loggerService = loggerService;
            this._service = Service;
        }
}

      

DependencyInstaller.cs

public class DependencyInstaller : IWindsorInstaller
    {
        public void Install(IWindsorContainer container, IConfigurationStore store)
        {
            container.Register(
                Component.For<ILogService>().ImplementedBy<LogService>().LifeStyle.PerWebRequest,
                Component.For<IDatabaseFactory>().ImplementedBy<DatabaseFactory>().LifeStyle.PerWebRequest,
                Component.For<IUnitOfWork>().ImplementedBy<UnitOfWork>().LifeStyle.PerWebRequest,
                AllTypes.FromThisAssembly().BasedOn<IHttpController>().LifestyleTransient(),
                AllTypes.FromAssemblyNamed("ISOS.Health.Service").Where(type => type.Name.EndsWith("Service")).WithServiceAllInterfaces().LifestylePerWebRequest(),
                AllTypes.FromAssemblyNamed("ISOS.Health.Repository").Where(type => type.Name.EndsWith("Repository")).WithServiceAllInterfaces().LifestylePerWebRequest()               
                );
        }
    }

      

If I use normal controller instead of ApiController then it gives me UserController.cs error

public class UserController : Controller
    {
        ILogService loggerService;
        IMAUserService _service;
        public UserController(ILogService loggerService, IMAUserService Service)
        {
            this.loggerService = loggerService;
            this._service = Service;
        }
}

      

This will give an error:

No parameterless constructor for this object

I am using CastleDI Windsor for dependency injection.

Do you need to do anything or register something?

+3


source to share


3 answers


FIRST APPROACH

Tip: Use with caution as it may leak Castle Windsor memory.

You need to create a controller activator that needs to implement an interface IControllerActivator

in order to use your DI container to create controller instances:

public class MyWindsorControllerActivator : IControllerActivator
{
    public MyWindsorControllerActivator(IWindsorContainer container)
    {
        _container = container;
    }

    private IWindsorContainer _container;

    public IController Create(RequestContext requestContext, Type controllerType)
    {
        return _container.Resolve(controllerType) as IController;
    }
}

      

Then add this class to yours DependencyInstaller

:

public class DependencyInstaller : IWindsorInstaller
{
    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        container.Register(
                // Current code...

                Component.For<IControllerActivator>()
                    .ImplementedBy<MyWindsorControllerActivator>()
                    .DependsOn(Dependency.OnValue("container", container))
                    .LifestyleSingleton();
            );
    }
}

      

Also create your own dependency transformer based on the Windsor container:

public class MyWindsorDependencyResolver : IDependencyResolver
{
    public MyWindsorDependencyResolver(IWindsorContainer container)
    {
        _container = container;
    }

    private IWindsorContainer _container;

    public object GetService(Type serviceType)
    {
        return _container.Resolve(serviceType);
    }

    public IEnumerable<object> GetServices(Type serviceType)
    {
        return _container.ResolveAll(serviceType).Cast<object>();
    }
}

      

Then finally, register your dependency transformer in a method Application_Start

in Global.asax.cs:

DependencyResolver.SetResolver(new MyWindsorDependencyResolver(windsorContainer));

      



This way, when MVC requires a controller activator through its dependency transformer, it will receive ours, which will use our Windsor container to create controllers with all its dependencies.

To avoid using memory leaks IControllerActivator

, the simplest solution would be to use a lifestyle like per thread or per web request rather than standard (Singleton), transient and combined for registered components. Check this link for more information on how to avoid memory leaks using the Castle Windsor container.

SECOND APPROACH

However, as @PhilDegenhardt pointed out, a much better and correct approach would be to implement a custom controller factory to be able to release the controller component created by the Castle Windsor DI container. You can find an example here (see Dependency Injection section) .

Taken from this example, the implementation could be:

Global.asax.cs:

public class MvcApplication : System.Web.HttpApplication
{
    private WindsorContainer _windsorContainer;

    protected void Application_Start()
    {
        var _windsorContainer = new WindsorContainer();
        _windsorContainer.Install(
            new DependencyInstaller(),
            // Other installers...
        );

        ControllerBuilder.Current.SetControllerFactory(new WindsorControllerFactory(_windsorContainer.Kernel));
    }

    protected void Application_End()
    {
        if (_windsorContainer != null)
        {
            _windsorContainer.Dispose();
        }
    }
}

      

WindsorControllerFactory.cs:

public class WindsorControllerFactory : DefaultControllerFactory
{
    private readonly IKernel _kernel;

    public WindsorControllerFactory(IKernel kernel)
    {
        _kernel = kernel;
    }

    public override void ReleaseController(IController controller)
    {
        _kernel.ReleaseComponent(controller);  // The important part: release the component
    }

    protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
    {
        if (controllerType == null)
        {
            throw new HttpException(404, string.Format("The controller for path '{0}' could not be found.", requestContext.HttpContext.Request.Path));
        }
        return (IController)_kernel.Resolve(controllerType);
    }
}

      

+5


source


Have a look at the following project link https://github.com/rarous/Castle.Windsor.Web.Mvc



Add this link via NuGet to your MVC project, it will do the registration job.

+2


source


Don't forget to catch your errors in global.asax.cs!

Check in:

container.Register(Component.For<IControllerFactory>().ImplementedBy<WindsorControllerFactory>());

      

MVC factory controller implementation:

using System;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using Castle.MicroKernel;

namespace Installer.Mvc
{
    public class WindsorControllerFactory : DefaultControllerFactory
    {
        private readonly IKernel _kernel;

        public WindsorControllerFactory(IKernel kernel)
        {
            _kernel = kernel;
        }

        protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
        {
            if (controllerType == null)
            {
                throw new HttpException(404, string.Format("The controller for path '{0}' could not be found.", requestContext.HttpContext.Request.Path));
            }

            if (_kernel.GetHandler(controllerType) != null)
            {
                return (IController)_kernel.Resolve(controllerType);
            }
            return base.GetControllerInstance(requestContext, controllerType);
        }

        public override void ReleaseController(IController controller)
        {
            _kernel.ReleaseComponent(controller);
        }
    }
}

      

0


source







All Articles