简体   繁体   English

如何在不使用Service Locator模式的情况下访问Ninject.Kernel

[英]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. 我已经阅读了很多关于这个主题的帖子,没有找到如何在不使用服务定位器模式的情况下访问Ninject.Kernel的明确指南。

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. 我目前在需要使用CustomerBusiness (这是我的服务)的类中有以下内容,并且它工作正常,但我很清楚它不是推荐的方法。

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. 如果有人能向我展示“CustomerFactory”或任何其他推荐的方法, 包括如何使用它,我将非常感激。

Update 更新

I am using ASP.NET Web Forms and need to access CustomerBusiness from CodeBehind. 我正在使用ASP.NET Web Forms,需要从CodeBehind访问CustomerBusiness

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? 我发现最终的解决方案是在这篇文章中得到最多投票的答案: 如何在asp.net Web窗体上实现Ninject或DI?

It looks like this (Note inheritance from PageBase, which is part of Ninject.Web - that is the key!): 它看起来像这样(注意继承自PageBase,它是Ninject.Web的一部分 - 这是关键!):

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. 由于您使用的是NinjectWebCommon ,我假设您有某种Web应用程序。 You really should only access the Ninject kernel in one place - at the composition root . 你真的应该只在一个地方访问Ninject内核 - 在组合根目录 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. 它是您构建对象图的地方,也是您唯一需要访问IoC容器的地方。 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. 例如,对于MVC Web应用程序,您有一个使用Ninject内核的控制器工厂,这是唯一引用它的地方。

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: 为了扩展您的特定情况,您的类将在其构造函数中接受ICustomerBusiness ,声明它需要一个ICustomerBusiness实例作为其依赖项:

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). 现在,无论哪个类使用ICustomerBusinessConsumer作为其依赖项,都将遵循相同的模式(接受ICustomerBusinessConsumer的实例作为其构造函数参数)。 You basically never construct your dependencies manually using new (specific exceptions aside). 您基本上不会使用new 手动构建依赖项 (除了特定的例外)。

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...) 组合根究竟是什么取决于您正在编写的应用程序的类型(控制台应用程序,WPF应用程序,Web服务,MVC Web应用程序......)


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. 编辑 :为了让自己熟悉ASP.NET WebForms领域的情况,我不得不查看细节,因为我从未使用它。 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. 遗憾的是,WebForms要求您在每个Page类中都有一个无参数构造函数,因此您无法从对象图的顶部一直到底部使用构造函数注入。

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: 然而,在咨询了Mark Seeman关于在WebForms中编写对象的章节后,我可以重新阐述如何处理这个框架的低效率,同时仍然遵循良好的DI实践:

  1. Have a class responsible for resolving the dependencies, using the Ninject's kernel you have set up. 使用您设置的Ninject内核,让一个类负责解析依赖项。 This may be a very thin wrapper around the kernel. 这可能是内核周围非常薄的包装器。 Let's call it DependencyContainer . 我们称之为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 . 假设您的页面类(我们称之为HomePage )依赖于ICustomerBusinessConsumer Then DependencyContainer has to allow us to retrieve an instance of ICustomerBusinessConsumer : 然后DependencyContainer必须允许我们检索ICustomerBusinessConsumer的实例:

     public ICustomerBusinessConsumer ResolveCustomerBusinessConsumer() { return Kernel.Get<ICustomerBusinessConsumer>(); } 
  4. Than in the MainPage class itself, you will resolve its dependencies in the default constructor: MainPage类本身相比,您将在默认构造函数中解析其依赖项:

     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. HttpContext中的依赖容器可用,一定不要把它当作服务定位器。 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. 实际上,这里的最佳实践(至少从对DI的真实角度来看)是拥有某种“实现者”类,您将在其中传递页面类的功能。

    For example, each action handled by MainPage will be only relayed to its implementor class. 例如, MainPage处理的每个操作都只会转发到其实现者类。 This implementor class will be a dependency of MainPage and, as all other dependencies, will be resolved using the container. 此实现者类将是MainPage的依赖项,并且与所有其他依赖项一样,将使用容器解析。

    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 重要的是,这些实现者类应该在不引用ASP.NET程序集的程序集中 ,因此没有机会访问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. 例如,在ASP.NET MVC应用程序中,这是以更好的方式处理的。 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. 在那里你有单点可以组成对象图,你不必像在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. 构造函数注入是DI与ninject的首选方法,但它也支持属性注入。 Take a read of the ninject page around injection patterns here https://github.com/ninject/ninject/wiki/Injection-Patterns . 在这里阅读注入模式的ninject页面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 当使用MVC时,所有的连接都是内置在nuget上的MVC ninject包中

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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