C# 如何在不使用服务定位器模式的情况下访问Ninject.Kernel

C# 如何在不使用服务定位器模式的情况下访问Ninject.Kernel,c#,dependency-injection,ninject,C#,Dependency Injection,Ninject,我已经阅读了几十篇关于这个主题的文章,但没有找到一条关于如何在不使用服务定位器模式的情况下访问Ninject.Kernel的清晰指南 我目前在需要使用CustomerBusiness(这是我的服务)的课程中有以下内容,而且效果很好,但我很清楚这不是推荐的方法 private CustomerBusiness _customerBusiness; private ICustomerRepository CustomerRepository { get { return NinjectWe

我已经阅读了几十篇关于这个主题的文章,但没有找到一条关于如何在不使用服务定位器模式的情况下访问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);
}
我读过很多关于使用工厂的建议,但是没有一个解释如何使用工厂。如果有人能告诉我“CustomerFactory”或任何其他推荐方法,包括如何使用它,我将不胜感激

更新

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

解决方案

我发现的最终解决方案是在这篇文章中获得最多选票的答案:

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


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

由于您使用的是
NinjectWebCommon
,我假设您有某种web应用程序。实际上,您应该只在一个位置访问Ninject内核,即在。它是构建对象图的地方,也是唯一需要访问IoC容器的地方。为了实际获得所需的依赖项,您通常使用

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

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

class CustomerBusinessConsumer : ICustomerBusinessConsumer
{
    private readonly ICustomerBusiness customerBusiness;

    public CustomerBusinessConsumer(ICustomerBusiness customerBusiness)
    {
        this.customerBusiness = customerBusiness;
    }
    ...
}
现在,无论哪个类使用
icCustomerBusinessConsumer
作为其依赖项,都将遵循相同的模式(接受
icCustomerBusinessConsumer
的实例作为其构造函数参数)。基本上,从不使用
new
(特定例外除外)手动构建依赖项

然后,您只需确保您的类获得它们的依赖项,而这正是您执行此操作的组合根。组合根的确切含义取决于您正在编写的应用程序的类型(控制台应用程序、WPF应用程序、web服务、MVC web应用程序…)


编辑:熟悉ASP.NET WebForms领域的情况 因为我从未使用过它,所以我不得不查看细节。不幸的是,WebForms要求在每个页面类中都有一个无参数构造函数,因此不能从对象图的顶部一直到底部使用构造函数注入

但是,在查阅了《在WebForms中组合对象》一章后,我可以重新表述如何处理此框架的低效性,同时仍然遵循良好的DI实践:

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

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

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

    public ICustomerBusinessConsumer ResolveCustomerBusinessConsumer()
    {
        return Kernel.Get<ICustomerBusinessConsumer>();
    }
    
  • 几点注意:

      >p>在 HtpValue中可用的依赖容器不能诱使它认为是服务定位器。事实上,这里的最佳实践(至少从忠实于DI的角度来看)是拥有某种“实现者”类,您将把页面类的功能传递给它们

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

      重要的一点是,这些实现器类应该位于不引用ASP.NET程序集的程序集中,因此没有机会访问
      HttpContext

    • 必须编写这么多代码来实现这一点当然不理想,但这只是框架局限性的结果。例如,在ASP.NET MVC应用程序中,这是一种更好的处理方法。在这里,您可以在一个点上组合对象图,而不必像在WebForms中那样在每个顶级类中解析它们

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


    构造函数注入是使用ninject的DI的首选方法,但它也支持属性注入。阅读关于注入模式的ninject页面

    这两种都是注入模式,而服务位置是基于请求的,根本不是真正的注入


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

    我在其他任何地方都使用构造函数注入,例如,存储库放在业务类的构造函数中,效果很好。但我必须从某个地方开始,对吗?我不能在只需要使用类的类中使用构造函数注入。@NielsBrinch通常在应用程序中只有一个入口点,在webapps中这是围绕global.ascx的,在控制台应用程序中这是围绕ar的
    protected void Application_Start(object sender, EventArgs e)
    {
       this.Application["container"] = new DependencyContainer();
    }
    
    public ICustomerBusinessConsumer ResolveCustomerBusinessConsumer()
    {
        return Kernel.Get<ICustomerBusinessConsumer>();
    }
    
    public MainPage()
    {
        var container = (DependencyContainer) HttpContext.Current.Application["container"];
        this.customerBusinessConsumer = container.ResolveCustomerBusinessConsumer();
    }