C# 将Ninject与ServiceLocater模式一起使用-好或坏

C# 将Ninject与ServiceLocater模式一起使用-好或坏,c#,dependency-injection,dependencies,ninject,modular-design,C#,Dependency Injection,Dependencies,Ninject,Modular Design,我快要习惯了。我了解依赖注入的原理,并且知道如何使用Ninject。但我现在有点困惑。当涉及到服务定位器模式时,意见会产生分歧 我的应用程序是建立在严格的模块化基础上的。我尽可能多地使用构造函数注入,虽然有点混乱(在我看来),但效果很好 现在,当一个扩展(外部代码)想要从这个系统中获益时,它不需要访问内核吗?我的意思是,现在我有一个静态类,可以访问应用程序的所有子系统。另一种方法是访问内核(服务定位器模式),并从中获取子系统依赖项。 在这里,我可以很容易地避免访问内核,或者更明确地说,不允许依赖

我快要习惯了。我了解依赖注入的原理,并且知道如何使用Ninject。但我现在有点困惑。当涉及到服务定位器模式时,意见会产生分歧

我的应用程序是建立在严格的模块化基础上的。我尽可能多地使用构造函数注入,虽然有点混乱(在我看来),但效果很好

现在,当一个扩展(外部代码)想要从这个系统中获益时,它不需要访问内核吗?我的意思是,现在我有一个静态类,可以访问应用程序的所有子系统。另一种方法是访问内核(服务定位器模式),并从中获取子系统依赖项。 在这里,我可以很容易地避免访问内核,或者更明确地说,不允许依赖内核

但是,如果扩展现在想要使用我的应用程序的任何组件(接口),从任何子系统,它将需要访问内核才能解析它们,因为只要不使用“kernel.Get()”,Ninject不会自动解析,对吗

Peww,很难用一种可以理解的方式来解释这一点。我希望你们能达到我的目标

为什么对内核或其包装器有依赖性会如此“糟糕”?我的意思是,你不能避免所有的依赖。例如,我的“核心”类仍然有一个访问所有子系统的类。 如果扩展想注册自己的模块以供进一步使用,该怎么办

我找不到任何好的答案来解释为什么这是一个糟糕的方法,但我经常读到它。此外,Ninject不像Unity或类似的框架那样使用这种方法


谢谢:)

关于这件事有宗教战争

当你提到服务定位器时,人们说的第一句话是:“但是如果我想更改我的容器呢?”。这个参数几乎总是无效的,因为“适当的”服务定位器可能足够抽象,允许您切换底层容器

也就是说,根据我的经验,使用服务定位器使代码难以使用。您的服务定位器必须到处传递,然后您的代码与服务定位器的存在紧密耦合

当您使用服务定位器时,您有两个主要的选项来以“解耦”(在这里松散地使用…)的方式维护模块

备选案文1:

将定位器传递到所有需要它的地方。本质上,这意味着您的代码变成了大量此类代码:

var locator = _locator;

var customerService = locator.Get<ICustomerService>();
var orders = customerService.GetOrders(locator, customerId); // locator again

// .. further down..
var repo = locator.Get<ICustomerRepository>();
var orderRepo = locator.Get<IOrderRepository>();

// ...etc...
var定位器=\u定位器;
var customerService=locator.Get();
var orders=customerService.GetOrders(定位器,customerId);//再次定位
// .. 再往下。。
var repo=locator.Get();
var orderepo=locator.Get();
//……等等。。。
备选案文2:

将所有代码分解为一个程序集,并在某处提供一个
公共静态
服务定位器。这更糟。。并最终与上面相同(只需直接调用服务定位器)


Ninject是幸运的(我的意思是幸运的——在Remo中有一个很棒的维护者/扩展者),因为它有一堆扩展,允许您在系统的几乎所有部分充分利用控制反转,消除我上面显示的代码。

这有点违反SO的政策,但为了扩展Simon的回答,我将引导您访问Mark Seeman的优秀博客帖子: 博客上的一些评论也很有趣

现在,为了解决您在编写ninject和扩展时遇到的问题(我假设您/编写它们时的合成根不知道这些问题),我想指出
NinjectModule
s。Ninject已经有了这样一种扩展机制,它被所有
Ninject.extension.XYZ
dll大量使用

您要做的是在扩展中实现一些
footextensionmodule:NinjectModule
类。模块包含
Bind
方法。现在,您将告诉ninject从一些.dll加载所有模块。就这样

这里有更详细的解释:

缺点:

  • 扩展依赖于Ninject
    • “更新ninject”时,可能需要重新编译扩展
    • 随着软件的发展,切换DI容器的成本将越来越高
  • 使用
    Rebind
    时,可能会出现难以追踪的问题(无论您是否使用模块,情况都是如此)
  • 特别是当扩展开发人员不知道其他扩展时,他们可能会创建相同或冲突的绑定(例如
    .Bind().ToConst(“Foo”)
    .Bind().ToConst(“Bar”)
    )。同样,如果您不使用模块,但扩展又增加了一层复杂性,也会出现这种情况
优点: -简单且切中要害的是,不需要额外的复杂/抽象层来抽象容器

我在一个不太小的应用程序(15k单元/组件测试)中使用了
NinjectModule
方法,并取得了很大成功

如果您需要的是简单绑定,比如<代码> .bdId()(to)(< /代码>,没有范围等),您也可以考虑使用一个更简单的系统,比如将属性放在类上,扫描这些,并在构图根中创建绑定。这种方法的功能要小得多,但正因为如此,它也更“可移植”(适合与其他DI容器一起使用)

依赖项注入是一个后期的实例化 合成根的思想是(只要可能)一次性创建所有对象(整个对象图)。例如,在
Main
方法中,您可能有
kernel.Get().Show()

然而,有时这是不可行或不恰当的
`.Bind<IFoo>().To<SomeExtensionsFoo>().WhenExtensionEnabled<SomeExtension>();`