DI - 创建服务对象

时间:2012-10-16 14:12:16

标签: c# asp.net-mvc-4 ninject

[注意:我的NinjectDependencyResolver类位于此问题的底部。]

设定:

在ASP.NET MVC 4应用程序中,我有使用服务的控制器。

每个服务都有一个构造函数参数,一个IUnitOfWork接口。 EG:

public ClientService(IUnitOfWork uow){ //... }

在Global.asax.cs类中注册一个适当编写的NinjectDependencyResolver : IDependencyResolver类,可以正确地实例化这些服务,并且一切正常。到目前为止,非常好。

但是,我有一些情况需要在其他一些代码中实例化服务,例如,在属性中。 EG:

namespace MyApp.Domain.Attributes
{
    public class AuthorizeUnprocessedOnlyAttribute : AuthorizeAttribute
    {
        protected override bool AuthorizeCore(HttpContextBase httpContext)
        {
            bool isUnprocessed = false;

            string usuario = string.Empty;
            if (httpContext.User.Identity.IsAuthenticated)
            {
                // THIS IS THE LINE WHERE DI IS NEEDED!!
                ClientService service = new ClientService();
                usuario = httpContext.User.Identity.Name;
                isUnprocessed = service.IsUnprocessed(usuario);
            }

            return isUnprocessed;
        }
    }
}

我的问题是这一行:

ClientService service = new ClientService();

我必须给ClientService一个无参数构造函数,如下所示:

public ClientService() : this (new UnitOfWork()) {}

这似乎完全反DI。

这样做的原因是我不确定如何在属性代码中注入依赖项。控制器由ControllerFactory实例化,所以我不知道这一点。

也就是说,我对Ninject或IDependencyResolver不太了解......

问题1:

那么,我怎样才能超越'Ignorance Pattern'

我应该按照以下方式获取ClientService对象:

NinjectDependencyResolver ndr = new NinjectDependencyResolver();
IUnitOfWorkMR uow = ndr.Kernel.Get<IUnitOfWorkMR>();
IClientService service = new ClientService(uow);

即使第一个问题的回答是“是”,我也不太高兴:

我仍然必须实例化ClientService的实例,所以现在我必须将IClientService接口绑定到NinjectDependencyResolver类中正确构造的ClientService实例。

这我不知道该怎么办,所以这是我的第二个问题:

问题2:

如何菊花链接IUnitOfWork构造参数的依赖注入以正确实例化ClientService? (请参阅我在下面注释掉的工作 - 我对它进行了评论,因为它对我来说甚至没有意义 - 我不应该创建一个新的UnitOfWork实例,我应该吗?):

NinjectDependencyResolver类:

namespace MyApp.Domain.Infrastructure
{
    public class NinjectDependencyResolver : IDependencyResolver
    {
        private IKernel kernel;

        public NinjectDependencyResolver()
        {
            kernel = new StandardKernel();
            AddBindings();
        }

        public object GetService(Type serviceType)
        {
            return kernel.TryGet(serviceType);
        }

        public IEnumerable<object> GetServices(Type serviceType)
        {
            return kernel.GetAll(serviceType);
        } 

        public IBindingToSyntax<T> Bind<T>()
        {
            return kernel.Bind<T>();
        }

        public IKernel Kernel
        {
            get { return kernel; }
        }

        private void AddBindings()
        {
            kernel.Bind<IUnitOfWorkMR>().To<UnitOfWorkMR>().InRequestScope();
            // kernel.Bind<IClientService>().To<ClientService>().WithConstructorArgument("uow", new UnitOfWork());
        }
    }
}

注意:

我更正了WithConstructorArgument中的错误(它缺少参数名称)。

编辑:

在测试中,令人难以置信的事情发生了:问题1中的代码有效。对于问题2,NinjectDepenedencyResolver类中的以下更改突然释放了一个工作应用程序(这样的事情确实是罕见的乐趣):

kernel.Bind<IUnitOfWorkMR>().To<UnitOfWorkMR>().InRequestScope();
            kernel.Bind<IClientService>().To<ClientService>().InRequestScope();

即,我删除了WithConstructorArgument调用。据推测,Ninject有一个UnitOfWork的工作实例,它使用它来实例化我的服务。所以,没有更多的无参数构造函数......

但这是RWTDI(正确的方法吗?)

1 个答案:

答案 0 :(得分:2)

是的,这是配置绑定的正确方法。这是DI容器的重点,它根据构造函数的参数和您配置的内容计算出要注入的内容。

但是,我发现你的NinjectDependcyResolver很奇怪。你为什么不只是使用Ninject.MVC3? (它也适用于MVC4)。这将自动为MVC设置DependencyResolver,你可以在App_Start \ NinjectWebCommon.cs中配置你的映射

对于属性,正确的方法是使用随Ninject.MVC3自动安装的BindFilter扩展。您可以这样配置:

kernel.BindFilter<MyAuthorization>(FilterScope.Action, 0)
    .WhenActionMethodHas<MyAuthorization>();

然后Ninject将自动处理注入其中的任何已配置对象。

这使得无需从属性内部调用GetService(这总是代码味道),并使所有内容协同工作。

至少,使用Ninject.MVC3设置你的MVC应用程序,然后在过滤器中,如果你真的想调用GetService,那么使用MVC DependencyResolver(Ninject.MVC自动注册)并执行类似这样的操作:

var x = DependencyResolver.Current.GetService<MyType>(); 

但是,你应该再使用BindFilter语法。