简体   繁体   中英

DI - Creating a service object

[NOTE: My NinjectDependencyResolver class is at the bottom of this question.]

Setup:

In an ASP.NET MVC 4 app I have controllers that consume services.

Each service has as a constructor paramater an IUnitOfWork interface. EG:

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

Registering an appropriately written NinjectDependencyResolver : IDependencyResolver class in the Global.asax.cs class allows these services to be properly instantiated and all works. So far, so good.

However, I have some cases where the service needs to be instantiated in some other bit of code, for example, in an attribute. 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;
        }
    }
}

My problem is the line:

ClientService service = new ClientService();

I have had to give ClientService a parameterless constructor, as follows:

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

This seems totally anti DI.

The reason for doing this is that I am not sure how to inject the dependency in the attribute code. Controllers are instantiated by the ControllerFactory, so I have got by without knowing about that.

Ie, I don't know enough about Ninject or IDependencyResolver...

Question 1:

So, how can I rise above my 'Ignorance Pattern' ?

Should I get my ClientService object along the lines of:

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

Even if the answer to this first question is "yes", I am not too happy:

I still have to instantiate an instance of ClientService, so now I have to bind the IClientService interface to a properly constructed ClientService instance in the NinjectDependencyResolver class.

This I do not know how to do, so this is my second question:

Question 2:

How do I daisy chain the dependency injection of the IUnitOfWork construction parameter to get ClientService properly instantiated? (See my commented out effort below - I commented it out because it doesn't even make sense to me -- I shouldn't be creating a new instance of UnitOfWork, should I?):

NinjectDependencyResolver class:

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

NOTE:

I have corrected the error in WithConstructorArgument (it was missing the parameter name).

EDIT:

On testing, the unbelievable has happened: the code in question 1 works. As to question 2, the following change in the NinjectDepenedencyResolver class unleashed a working app out of the blue (such things are rare joys indeed):

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

ie, I removed the WithConstructorArgument call. Presumably, Ninject has a working instance of UnitOfWork and it uses that to instantiate my service. So, no more parameterless constructors...

But is this the RWTDI (The Right Way To Do It)?

Yes, that is the correct way to configure your bindings. That's the whole point of a DI container, it figures out what to inject based on the parameters of the constructor and what you have configured.

However, I find your NinjectDependcyResolver to be odd. Why aren't you just using Ninject.MVC3? (it works for MVC4 as well). This will automatically setup the DependencyResolver for MVC and you would configure your mappings in App_Start\\NinjectWebCommon.cs

In the case of your Attribute, the correct way is to use the BindFilter extension that gets automatically installed with Ninject.MVC3. You would configure it like this:

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

Then Ninject will deal with any configured objects injected into it automatically.

This negates the need to call GetService from inside the attribute (which is always a code smell), and makes everything work together.

At a bare minimum, use Ninject.MVC3 to setup your MVC app, then in the filter, if you really wanted to call GetService, then use the MVC DependencyResolver (which Ninject.MVC automatically registers with) and do something like this:

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

But again, you should really use the BindFilter syntax instead.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM