ServiceStack IoC/DI:在容器中注册 类 - 如何注册所有实现特定接口的

ServiceStack IoC/DI: Registering classes in the Container - how to register all that implements a specific interface

我已经开始更仔细地研究 ServiceStack IoC/DI,到目前为止它工作得非常好,例如当我使用手动注册方法时:

container.AddScoped<IGeoService, GeoService>();

我在服务中的 属性 然后按预期填充:

public class MyNiceService : Service
{
    public IGeoService Geo { get; set; }
}

以上工作正常并且 Geo 已正确填充。

第一个想法是:

自动注册实现接口的所有 classes

最好,我想找到所有实现特定接口的 classes,并在容器中注册所有这些 classes,例如:

GetType().Assembly.GetTypes()
        .Where(x => x is IMyCoreService && x.IsClass && !x.IsAbstract)
        .Each(x => container.RegisterAutoWiredType(x));

但这失败了,然后 public GeoService Geo { get; set; } 为空。

感谢您的意见,所以我正在以正确的方式做事:-)

当您注册您的依赖项时,它总是针对一个类型进行注册。如果您没有指定接口,那么它将针对您需要在依赖属性中使用的具体类型进行注册。同样,如果您针对其接口注册依赖项,则需要在依赖项属性中使用该接口。

没有针对其实现的所有接口注册具体类型的隐含行为,注册的任何类型都是您需要用来解析它的类型。

如果你注册一个具体的依赖:

container.RegisterAutoWiredType(typeof(GeoService));

然后它将被注入到引用其具体类型的服务中 属性:

public GeoService Geo { get; set; }

如果您说没有,那么我假设您的查询没有注册类型。

不是真正的答案,而是部分答案集中在 “为什么有一个选项可以同时指定接口和具体 class?”。 =19=]

(计数器)示例

想象一下,除了您当前的代码之外,您还有这个 class:

public class OtherGeoService : IGeoService { ... }

和这个 DI 设置:

container.AddScoped<GeoService>();
container.AddScoped<OtherGeoService>();

那么问题就变成了“class的实例应该作为IGeoService注入?”。从 DI 框架的角度来看,这个问题没有简单的答案(尽管从你的角度来看可能是这样),这就是为什么在大多数框架中你有责任回答这个问题。所以通过写

container.AddScoped<IGeoService, GeoService>();

你基本上是在说 “如果有人要求 IGeoService,给他们一个 GeoService

一枚硬币的正反面

另一方面,人们通常在 DI 容器中注册的许多 classes 实现了各种通用接口,如 IEnumerableIDisposableISerializable。如果在行动中有一些精心设计的实现选择算法1,将很难确定(或至少不明显)将注入哪种对象的确切类型。

回顾

我认为这是大多数(如果不是全部)DI 框架需要这种“冗长”的依赖项注册的两个主要原因。您只能得到您所要求的,而不是 “技术上 'fits' 您的要求的下一个最好的东西”。请注意,决定不受请求代码控制,因此不违反 IoC 范例。

好的,但为什么?

我知道对于刚开始进入编程世界的开发人员来说,over-engineered 似乎 day-to-day 使用它。我的意思是,问 “为什么我要提供不止一种服务实现?” 是完全合理的,归结为 “我为什么要费心定义所有这些样板接口是否只有一种实现?".

在许多(也许甚至是大多数)情况下,您不必这样做。但无论如何都有充分的理由这样做:

  • 它源于SOLID编程原则中的“O”。一个接口只提供了“服务能做什么”而不是“它究竟是怎么做的”的抽象。这样,您的服务就其业务逻辑而言就可以清楚地分开。起初这似乎不太可能,但很有可能在某个时候您需要 modify/replace 部分应用程序以满足新需求。
  • 它在编写单元测试时非常方便。如果它是一个接口,您可以轻松地模拟一个依赖项。出于测试目的模拟具体 classes 更加混乱(如果可能的话)。2

1 我个人认为不应该有这样的算法,或者至少你应该尽量不要依赖它们。这样你就可以几乎无痛地切换 DI 框架而没有任何副作用。

2 人们普遍认为单元测试不应该影响被测单元的设计,即你不应该仅仅为了允许或简化它而引入依赖或暴露任何功能测试。