简体   繁体   中英

How to access Ninject.Kernel without using Service Locator pattern

I have read dozens of posts regarding this topic, without finding a clear guideline of how to access the Ninject.Kernel without using the Service Locator pattern.

I currently have the following in the classes that need to use CustomerBusiness (which is my service) and it works fine, but I am well aware that it is not the recommended way of doing it.

private CustomerBusiness _customerBusiness;

private ICustomerRepository CustomerRepository
{
    get { return NinjectWebCommon.Kernel.Get<IAccountRepository>(); }
}

private CustomerBusiness CustomerBusiness
{
    get
    {
        if (_customerBusiness == null)
        {
            _customerBusiness = new CustomerBusiness(AccountRepository);
        }

        return _customerBusiness;
    }
}

public Customer GetCustomer(int id)
{
    return CustomerBusiness.GetCustomer(id);
}

This is the Kernel property accessed in the code above:

public static IKernel Kernel
{
    get
    {
        return CreateKernel();
    }
}

I've read many suggestions about using a factory for this, but none of them explain how to use this factory. I would really appreciate if anyone could show me the "CustomerFactory" or any other recommended approach including how to use it.

Update

I am using ASP.NET Web Forms and need to access CustomerBusiness from CodeBehind.

Solution

The final solution that I found to be working, was the answer with the most votes found at this post: How can I implement Ninject or DI on asp.net Web Forms?

It looks like this (Note inheritance from PageBase, which is part of Ninject.Web - that is the key!):

public partial class Edit : PageBase
{
    [Inject]
    public ICustomerBusiness CustomerBusiness { get; set; }
    ...

The accepted answer below indirectly lead me to find this solution.

Since you're using NinjectWebCommon , I assume you have a web application of some sort. You really should only access the Ninject kernel in one place - at the composition root . It is the place where you are building the object graph and the only place you should ever need an access to the IoC container. To actually get the dependencies you need, you typically employ constructor injection .

In case of MVC web applications, for example, you have a controller factory using the Ninject kernel and that's the only place which references it.

To expand on your particular situation, your class would accept ICustomerBusiness in its constructor, declaring that it needs an instance of ICustomerBusiness as its dependency:

class CustomerBusinessConsumer : ICustomerBusinessConsumer
{
    private readonly ICustomerBusiness customerBusiness;

    public CustomerBusinessConsumer(ICustomerBusiness customerBusiness)
    {
        this.customerBusiness = customerBusiness;
    }
    ...
}

Now, whichever class uses ICustomerBusinessConsumer as its dependency, would follow the same pattern (accepting an instance of ICustomerBusinessConsumer as its constructor parameter). You basically never construct your dependencies manually using new (specific exceptions aside).

Then, you just have to make sure your classes get their dependencies and it's this composition root where you do this. What exactly is composition root depends on the type of an application you are writing (console application, WPF application, web service, MVC web application...)


EDIT : To get myself familiar with the situation in the ASP.NET WebForms realm I had to look up the details since I've never used it. Unfortunately, WebForms require you to have a parameterless constructor at each of your Page classes, so you can't use constructor injection all the way from the top of the object graph down to the bottom.

However, after consulting Mark Seeman 's chapter on composing objects in WebForms, I can rephrase how to deal with this framework's inefficiency, while still acting in line with good DI practices:

  1. Have a class responsible for resolving the dependencies, using the Ninject's kernel you have set up. This may be a very thin wrapper around the kernel. Let's call it DependencyContainer .

  2. Create your container and save it in the application context, so that it's ready when you need it

     protected void Application_Start(object sender, EventArgs e) { this.Application["container"] = new DependencyContainer(); } 
  3. Let's suppose your page class (let's call it HomePage ) has a dependency on ICustomerBusinessConsumer . Then DependencyContainer has to allow us to retrieve an instance of ICustomerBusinessConsumer :

     public ICustomerBusinessConsumer ResolveCustomerBusinessConsumer() { return Kernel.Get<ICustomerBusinessConsumer>(); } 
  4. Than in the MainPage class itself, you will resolve its dependencies in the default constructor:

     public MainPage() { var container = (DependencyContainer) HttpContext.Current.Application["container"]; this.customerBusinessConsumer = container.ResolveCustomerBusinessConsumer(); } 

Few notes:

  • having the dependency container available in the HttpContext must not be tempting to consider it a service locator. In fact, the best practice here (at least from the standpoint of being true to DI) is to have some kind of "implementor" classes to which you will relay the functionality of your page classes.

    For example, each action handled by MainPage will be only relayed to its implementor class. This implementor class will be a dependency of MainPage and, as all other dependencies, will be resolved using the container.

    The important part is that these implementor classes should be in assemblies not referencing the ASP.NET assemblies and thus without a chance to access the HttpContext

  • having to write so much code to achieve this is certainly not ideal, but it is only a consequence of the framework's limitation. In ASP.NET MVC applications, for example, this is dealt with in a much better way. There you have single point where you can compose the object graphs and you don't have to resolve them in each top-level class as you have to in WebForms.

  • the good thing is that while you have to write some plumbing code in the page class constructors, from there down the object graph you can employ constructor injection

Constructor injection is the preferred method for DI with ninject, however it also supports property injection. Take a read of the ninject page around injection patterns here https://github.com/ninject/ninject/wiki/Injection-Patterns .

Both of these are injection patterns unlike service location which is request based, not really injection at all.

The flipside of injection is that you need to control construction. When using MVC all of the wiring to do this is built into the MVC ninject package on nuget

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