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

时间:2012-08-12 10:15:21

标签: c# dependency-injection ninject

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

我目前在需要使用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);
}

这是上面代码中访问的内核属性:

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

我已经阅读了许多关于使用工厂的建议,但没有一个解释如何使用这个工厂。如果有人能够向我展示“CustomerFactory”或任何其他推荐方法包括如何使用,我将非常感激。

更新

我正在使用ASP.NET Web窗体,需要从CodeBehind访问CustomerBusiness

解决方案

我发现最终的解决方案是在这篇文章中获得最多投票的答案: How can I implement Ninject or DI on asp.net Web Forms?

它看起来像这样(注意继承自PageBase,它是Ninject.Web的一部分 - 这是关键!):

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

下面接受的答案间接地让我找到了这个解决方案。

2 个答案:

答案 0 :(得分:12)

由于您使用的是NinjectWebCommon,我假设您拥有某种类型的Web应用程序。你真的应该只在一个地方访问Ninject内核 - 在composition root。它是您构建对象图的地方,也是您唯一需要访问IoC容器的地方。要真正获得所需的依赖项,通常使用constructor injection

例如,对于MVC Web应用程序,您有一个使用Ninject内核的控制器工厂,这是唯一引用它的地方。

要扩展您的特定情况,您的类将在其构造函数中接受ICustomerBusiness,声明它需要ICustomerBusiness的实例作为其依赖项:

class CustomerBusinessConsumer : ICustomerBusinessConsumer
{
    private readonly ICustomerBusiness customerBusiness;

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

现在,无论哪个类使用ICustomerBusinessConsumer作为其依赖项,都将遵循相同的模式(接受ICustomerBusinessConsumer的实例作为其构造函数参数)。您基本上永远不会使用new手动构建您的依赖项(除了特定的例外)。

然后,您只需要确保您的类获得它们的依赖关系,并且这是您执行此操作的组合根。组合根究竟是什么取决于您正在编写的应用程序的类型(控制台应用程序,WPF应用程序,Web服务,MVC Web应用程序......)


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

然而,在咨询Mark Seeman关于在WebForms中编写对象的章节之后,我可以重新说明如何处理这个框架的低效率,同时仍然遵循良好的DI实践:

  1. 使用您设置的Ninject内核,让一个类负责解析依赖项。这可能是内核周围非常薄的包装器。我们称之为DependencyContainer

  2. 创建容器并将其保存在应用程序上下文中,以便在需要时准备就绪

    protected void Application_Start(object sender, EventArgs e)
    {
       this.Application["container"] = new DependencyContainer();
    }
    
  3. 我们假设您的网页类(我们称之为HomePage)依赖于ICustomerBusinessConsumer。然后DependencyContainer必须允许我们检索ICustomerBusinessConsumer的实例:

    public ICustomerBusinessConsumer ResolveCustomerBusinessConsumer()
    {
        return Kernel.Get<ICustomerBusinessConsumer>();
    }
    
  4. MainPage类本身中,您将在默认构造函数中解析其依赖项:

    public MainPage()
    {
        var container = (DependencyContainer) HttpContext.Current.Application["container"];
        this.customerBusinessConsumer = container.ResolveCustomerBusinessConsumer();
    }
    
  5. 很少注意到:

    • HttpContext中的依赖项容器可用,不能将它视为服务定位器。事实上,这里的最佳实践(至少从对DI的真实角度来看)是拥有某种“实现者”类,您将在其中传递页面类的功能。

      例如,MainPage处理的每个操作都只会转发到其实现者类。此实现者类将是MainPage的依赖项,并且与所有其他依赖项一样,将使用容器解析。

      重要的是,这些实现者类应该在不引用ASP.NET程序集的程序集中,因此无法访问HttpContext

    • 必须编写如此多的代码来实现这一点当然不是理想的,但这只是框架限制的结果。例如,在ASP.NET MVC应用程序中,这是以更好的方式处理的。在那里你有单点可以组成对象图,你不必像在WebForms中那样在每个顶级类中解决它们。

    • 好消息是,虽然你必须在页面类构造函数中编写一些管道代码,但是从那里开始,你可以使用构造函数注入

答案 1 :(得分:1)

构造函数注入是使用ninject的DI的首选方法,但它也支持属性注入。在这里阅读注入模式的ninject页面https://github.com/ninject/ninject/wiki/Injection-Patterns

这两种注射模式都不同于基于请求的服务位置,而不是真正的注射。

注射的另一面是你需要控制结构。当使用MVC时,所有的连接都是内置在nuget

上的MVC ninject包中