c# - ServiceStack IoC/DI:在容器中注册类 - 如何注册所有实现特定接口的类
问题描述
我已经开始更仔细地研究 ServiceStack IoC/DI,到目前为止,它工作得非常好,例如,当我使用手动注册方法时:
container.AddScoped<IGeoService, GeoService>();
然后按预期填充我在服务中的属性:
public class MyNiceService : Service
{
public IGeoService Geo { get; set; }
}
以上工作并Geo
正确填充。
第一个想法是:
- 为什么可以选择同时指定接口和具体类?
- 如果我只是将它添加到容器中,
container.AddScoped<GeoService>();
那么它应该“适合”该属性,因为 GeoService 实现了 IGeoService? - 但是,如果我使用
container.AddScoped<GeoService>();
,那么 DI 将失败,并且我在 Services 中的 IGeoService 属性为空。 - 如果我将属性更改为
public GeoService Geo { get; set; }
,则 DI 有效。
自动注册所有实现接口的类
最好,我想找到实现特定接口的所有类,并在容器中注册所有这些类,例如:
GetType().Assembly.GetTypes()
.Where(x => x is IMyCoreService && x.IsClass && !x.IsAbstract)
.Each(x => container.RegisterAutoWiredType(x));
但这失败了,public GeoService Geo { get; set; }
然后为空。
我很感激一些意见,所以我以正确的方式做事:-)
解决方案
不是一个真正的答案,而是一个专注于“为什么可以选择同时指定接口和具体类?”的部分答案。.
(反)例子
想象一下,除了您当前的代码之外,您还有这个类:
public class OtherGeoService : IGeoService { ... }
这个 DI 设置:
container.AddScoped<GeoService>();
container.AddScoped<OtherGeoService>();
那么问题就变成了“应该注入哪个类的实例IGeoService
?” . 从 DI 框架的角度来看,这个问题没有简单的答案(尽管从您的角度来看可能),这就是为什么在大多数框架中回答这个问题是您的责任。所以通过写
container.AddScoped<IGeoService, GeoService>();
您基本上是在说“如果有人要求IGeoService
,请给他们GeoService
”。
同一枚硬币的第二面
另一方面,人们通常在 DI 容器中注册的许多类都实现了各种通用接口,例如IEnumerable
、IDisposable
或ISerializable
. 如果在动作1中有一些精心设计的实现选择算法,那么将很难确定(或至少不明显)将注入什么确切类型的对象。
回顾
我相信这是为什么大多数(如果不是全部)DI 框架需要这种“冗长”的依赖注册的两个主要原因。你只会得到你所要求的东西,而不是“技术上‘符合’你要求的下一个最好的东西”。请注意,该决定不受请求代码的控制,因此不违反IoC范式。
好的,但是为什么?
我知道,对于刚开始进入编程世界的开发人员来说,它对于日常使用来说似乎是过度设计的。我的意思是,问“为什么我要提供不止一个服务实现”是完全合理的。,归结为“如果它们只有一个实现,我为什么还要费心定义所有这些样板接口?” .
在许多(甚至可能是大多数)情况下,您不必这样做。但无论如何都有充分的理由这样做:
- 它源于 SOLID 编程原则中的“O”。接口仅提供“服务可以做什么”的抽象,而不是“它究竟是如何做到的”的抽象。这样,您的服务就业务逻辑而言是明确分离的。起初这似乎不太可能,但很有可能在某些时候您将需要修改/替换应用程序的某些部分以满足新的需求。
- 它在编写单元测试时非常方便。如果它是一个接口,您可以轻松地模拟一个依赖项。为了测试目的而模拟具体类要麻烦得多(如果可能的话)。2
1我个人认为不应该有这样的算法,或者至少你应该尽量不依赖它们。这样一来,您几乎可以轻松地切换 DI 框架,而不会产生任何副作用。
2人们普遍认为,单元测试不应影响被测单元的设计,即您不应仅仅为了允许或简化其测试而引入依赖项或公开任何功能。
推荐阅读
- java - bufferedwriter 拒绝写入文本文件(Java)
- logging - Kotlin 中的全局记录器
- c - 文件流已打开,但没有任何内容打印到控制台。程序似乎没有“完成”
- migration - 是否可以将现有的 Google 和 Facebook OAuth 用户迁移到新的 AWS Cognito 用户池?
- post - 发送发布请求时,类型“int”不是类型转换中“String”类型的子类型
- docker - 在计算引擎上重新部署 docker 容器的正确步骤是什么?
- c# - 获取字符串中的特定句子
- javascript - 如何计算购物车产品运费
- sockets - 查找服务器是否连接到客户端
- python - 词频+可视化+python