如何在Controller中使用依赖注入

时间:2015-05-15 11:52:13

标签: asp.net-mvc castle-windsor

我有以下代码,可以正常工作 的 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()               
                );
        }
    }

如果我使用普通的控制器而不是ApiController,那么它会给我一个错误 的 UserController.cs

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

这会出错:

  

没有为此对象定义无参数构造函数

我使用CastleDI Windsor进行依赖注入。

我是否需要做任何事情或注册?

3 个答案:

答案 0 :(得分:5)

第一种方法

建议:请谨慎使用,因为它可能会导致Castle Windsor内存泄漏。

您必须创建一个控制器激活器,它应该实现IControllerActivator接口,以便使用您的DI容器来创建控制器实例:

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;
    }
}

然后,将此课程添加到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();
            );
    }
}

另外,根据Windsor容器创建自己的依赖项解析器:

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>();
    }
}

然后,最后,在Global.asax.cs中的Application_Start方法中注册依赖项解析器:

DependencyResolver.SetResolver(new MyWindsorDependencyResolver(windsorContainer));

这样,当MVC需要控制器激活器通过它的依赖解析器时,它将获得我们的,它将使用我们的Windsor容器来创建具有其所有依赖性的控制器。

为了避免使用IControllerActivator导致内存泄漏,最简单的解决方案是使用每个线程或每个Web请求的生活方式,而不是默认(Singleton),瞬态和池化,用于已注册的组件。有关如何使用Castle Windsor Container避免内存泄漏的详细信息,请查看this link

第二种方法

然而,正如@PhilDegenhardt所指出的,更好更正确的方法是实现自定义控制器工厂,以便能够释放由Castle Windsor DI Container创建的控制器组件。 Here you can find an example (see the section about Dependency Injection)

从该示例中获取,实现可能是:

<强>的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);
    }
}

答案 1 :(得分:2)

查看以下项目链接https://github.com/rarous/Castle.Windsor.Web.Mvc

通过NuGet将此引用添加到您的MVC项目中,它将为您执行注册工作。

答案 2 :(得分:0)

不要忘记在global.asax.cs中捕获错误!

注册:

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

MVC控制器工厂的实施:

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);
        }
    }
}