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
已正确填充。
第一个想法是:
- 为什么有一个选项可以同时指定接口和具体 class?
- 如果我只是使用
container.AddScoped<GeoService>();
将它添加到容器中,那么它应该 'fit' 属性,因为 GeoService 实现了 IGeoService?
- 但是,如果我使用
container.AddScoped<GeoService>();
,那么DI会失败,我的服务中的IGeoService属性为null。
- 如果我将 属性 更改为
public GeoService Geo { get; set; }
,DI 就可以工作了。
自动注册实现接口的所有 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 实现了各种通用接口,如 IEnumerable
、IDisposable
或 ISerializable
。如果在行动中有一些精心设计的实现选择算法1,将很难确定(或至少不明显)将注入哪种对象的确切类型。
回顾
我认为这是大多数(如果不是全部)DI 框架需要这种“冗长”的依赖项注册的两个主要原因。您只能得到您所要求的,而不是 “技术上 'fits' 您的要求的下一个最好的东西”。请注意,决定不受请求代码控制,因此不违反 IoC 范例。
好的,但为什么?
我知道对于刚开始进入编程世界的开发人员来说,over-engineered 似乎 day-to-day 使用它。我的意思是,问 “为什么我要提供不止一种服务实现?” 是完全合理的,归结为 “我为什么要费心定义所有这些样板接口是否只有一种实现?".
在许多(也许甚至是大多数)情况下,您不必这样做。但无论如何都有充分的理由这样做:
- 它源于SOLID编程原则中的“O”。一个接口只提供了“服务能做什么”而不是“它究竟是怎么做的”的抽象。这样,您的服务就其业务逻辑而言就可以清楚地分开。起初这似乎不太可能,但很有可能在某个时候您需要 modify/replace 部分应用程序以满足新需求。
- 它在编写单元测试时非常方便。如果它是一个接口,您可以轻松地模拟一个依赖项。出于测试目的模拟具体 classes 更加混乱(如果可能的话)。2
1 我个人认为不应该有这样的算法,或者至少你应该尽量不要依赖它们。这样你就可以几乎无痛地切换 DI 框架而没有任何副作用。
2 人们普遍认为单元测试不应该影响被测单元的设计,即你不应该仅仅为了允许或简化它而引入依赖或暴露任何功能测试。
我已经开始更仔细地研究 ServiceStack IoC/DI,到目前为止它工作得非常好,例如当我使用手动注册方法时:
container.AddScoped<IGeoService, GeoService>();
我在服务中的 属性 然后按预期填充:
public class MyNiceService : Service
{
public IGeoService Geo { get; set; }
}
以上工作正常并且 Geo
已正确填充。
第一个想法是:
- 为什么有一个选项可以同时指定接口和具体 class?
- 如果我只是使用
container.AddScoped<GeoService>();
将它添加到容器中,那么它应该 'fit' 属性,因为 GeoService 实现了 IGeoService? - 但是,如果我使用
container.AddScoped<GeoService>();
,那么DI会失败,我的服务中的IGeoService属性为null。 - 如果我将 属性 更改为
public GeoService Geo { get; set; }
,DI 就可以工作了。
自动注册实现接口的所有 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 实现了各种通用接口,如 IEnumerable
、IDisposable
或 ISerializable
。如果在行动中有一些精心设计的实现选择算法1,将很难确定(或至少不明显)将注入哪种对象的确切类型。
回顾
我认为这是大多数(如果不是全部)DI 框架需要这种“冗长”的依赖项注册的两个主要原因。您只能得到您所要求的,而不是 “技术上 'fits' 您的要求的下一个最好的东西”。请注意,决定不受请求代码控制,因此不违反 IoC 范例。
好的,但为什么?
我知道对于刚开始进入编程世界的开发人员来说,over-engineered 似乎 day-to-day 使用它。我的意思是,问 “为什么我要提供不止一种服务实现?” 是完全合理的,归结为 “我为什么要费心定义所有这些样板接口是否只有一种实现?".
在许多(也许甚至是大多数)情况下,您不必这样做。但无论如何都有充分的理由这样做:
- 它源于SOLID编程原则中的“O”。一个接口只提供了“服务能做什么”而不是“它究竟是怎么做的”的抽象。这样,您的服务就其业务逻辑而言就可以清楚地分开。起初这似乎不太可能,但很有可能在某个时候您需要 modify/replace 部分应用程序以满足新需求。
- 它在编写单元测试时非常方便。如果它是一个接口,您可以轻松地模拟一个依赖项。出于测试目的模拟具体 classes 更加混乱(如果可能的话)。2
1 我个人认为不应该有这样的算法,或者至少你应该尽量不要依赖它们。这样你就可以几乎无痛地切换 DI 框架而没有任何副作用。
2 人们普遍认为单元测试不应该影响被测单元的设计,即你不应该仅仅为了允许或简化它而引入依赖或暴露任何功能测试。