我已经阅读了几十篇关于这个主题的帖子,没有找到如何在不使用服务定位器模式的情况下访问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 Forms并需要访问 CustomerBusiness
来自CodeBehind。
解
我找到的最终解决方案是在这篇文章中找到的票数最多的答案:
如何在asp.net Web窗体上实现Ninject或DI?
它看起来像这样(注意继承自PageBase,它是Ninject.Web的一部分 - 这是关键!):
public partial class Edit : PageBase
{
[Inject]
public ICustomerBusiness CustomerBusiness { get; set; }
...
下面接受的答案间接导致我找到这个解决方案。
因为你正在使用 NinjectWebCommon
,我假设您有某种Web应用程序。你真的应该只在一个地方访问Ninject内核 - 在 组成根。它是您构建对象图的地方,也是您唯一需要访问IoC容器的地方。要实际获得所需的依赖项,通常需要使用 构造函数注入。
例如,对于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类中都有一个无参数构造函数,因此您无法从对象图的顶部一直到底部使用构造函数注入。
但经过咨询后 马克西曼关于在WebForms中编写对象的章节,我可以重新说明如何处理这个框架的低效率,同时仍然遵循良好的DI实践:
使用您设置的Ninject内核,让一个负责解析依赖关系的类。这可能是内核周围非常薄的包装器。我们称之为 DependencyContainer
。
创建容器并将其保存在应用程序上下文中,以便在需要时准备好
protected void Application_Start(object sender, EventArgs e)
{
this.Application["container"] = new DependencyContainer();
}
让我们假设您的页面类(让我们称之为 HomePage
)依赖于 ICustomerBusinessConsumer
。然后 DependencyContainer
必须允许我们检索一个实例 ICustomerBusinessConsumer
:
public ICustomerBusinessConsumer ResolveCustomerBusinessConsumer()
{
return Kernel.Get<ICustomerBusinessConsumer>();
}
比在 MainPage
类本身,您将在默认构造函数中解析其依赖项:
public MainPage()
{
var container = (DependencyContainer) HttpContext.Current.Application["container"];
this.customerBusinessConsumer = container.ResolveCustomerBusinessConsumer();
}
几点说明:
让依赖容器可用于 HttpContext
一定不要把它当作服务定位器。实际上,这里的最佳实践(至少从对DI的真实角度来看)是拥有某种“实现者”类,您将在其中传递页面类的功能。
例如,每个动作由...处理 MainPage
将只传递给它的实现者类。这个实现者类将是一个依赖 MainPage
并且,与所有其他依赖项一样,将使用容器解析。
重要的是这些实现者类 应该在不引用ASP.NET程序集的程序集中 因而没有机会进入 HttpContext
必须编写如此多的代码来实现这一点当然不是理想的,但这只是框架限制的结果。例如,在ASP.NET MVC应用程序中,这是以更好的方式处理的。在那里你有单点可以组成对象图,你不必像在WebForms中那样在每个顶级类中解析它们。
好的是,虽然你必须在页面类构造函数中编写一些管道代码,但从那里开始,对象图可以使用构造函数注入
因为你正在使用 NinjectWebCommon
,我假设您有某种Web应用程序。你真的应该只在一个地方访问Ninject内核 - 在 组成根。它是您构建对象图的地方,也是您唯一需要访问IoC容器的地方。要实际获得所需的依赖项,通常需要使用 构造函数注入。
例如,对于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类中都有一个无参数构造函数,因此您无法从对象图的顶部一直到底部使用构造函数注入。
但经过咨询后 马克西曼关于在WebForms中编写对象的章节,我可以重新说明如何处理这个框架的低效率,同时仍然遵循良好的DI实践:
使用您设置的Ninject内核,让一个负责解析依赖关系的类。这可能是内核周围非常薄的包装器。我们称之为 DependencyContainer
。
创建容器并将其保存在应用程序上下文中,以便在需要时准备好
protected void Application_Start(object sender, EventArgs e)
{
this.Application["container"] = new DependencyContainer();
}
让我们假设您的页面类(让我们称之为 HomePage
)依赖于 ICustomerBusinessConsumer
。然后 DependencyContainer
必须允许我们检索一个实例 ICustomerBusinessConsumer
:
public ICustomerBusinessConsumer ResolveCustomerBusinessConsumer()
{
return Kernel.Get<ICustomerBusinessConsumer>();
}
比在 MainPage
类本身,您将在默认构造函数中解析其依赖项:
public MainPage()
{
var container = (DependencyContainer) HttpContext.Current.Application["container"];
this.customerBusinessConsumer = container.ResolveCustomerBusinessConsumer();
}
几点说明:
让依赖容器可用于 HttpContext
一定不要把它当作服务定位器。实际上,这里的最佳实践(至少从对DI的真实角度来看)是拥有某种“实现者”类,您将在其中传递页面类的功能。
例如,每个动作由...处理 MainPage
将只传递给它的实现者类。这个实现者类将是一个依赖 MainPage
并且,与所有其他依赖项一样,将使用容器解析。
重要的是这些实现者类 应该在不引用ASP.NET程序集的程序集中 因而没有机会进入 HttpContext
必须编写如此多的代码来实现这一点当然不是理想的,但这只是框架限制的结果。例如,在ASP.NET MVC应用程序中,这是以更好的方式处理的。在那里你有单点可以组成对象图,你不必像在WebForms中那样在每个顶级类中解析它们。
好的是,虽然你必须在页面类构造函数中编写一些管道代码,但从那里开始,对象图可以使用构造函数注入
构造函数注入是DI与ninject的首选方法,但它也支持属性注入。在这里阅读有关注射模式的ninject页面 https://github.com/ninject/ninject/wiki/Injection-Patterns。
这两种都是不同于基于请求的服务位置的注入模式,而不是真正的注入。
注射的另一面是你需要控制结构。当使用MVC时,所有的连接都是内置在nuget上的MVC ninject包中