在Microsofts依赖注入中获取开放式通用服务
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在Microsofts依赖注入中获取开放式通用服务相关的知识,希望对你有一定的参考价值。
假设我们有以下服务:
interface IService { }
interface IService<T> : IService {
T Get();
}
在ASP.Net-Core中,在我们使用不同的T
注册了一些实现后,我们可以获得所有这样的注册服务:
IEnumerable<IService> services = serviceProvider.GetServices<IService>();
现在,因为我需要从另一个不是选项的接口访问泛型类型参数。如何在不丢失泛型类型的情况下检索IService<T>
的所有实现?就像是:
IEnumerable<IService<T>> services = serviceProvider.GetServices<IService<T>>();
foreach (var s in services) {
Method(s);
}
// Here we have a generic method I don't have control over.
// I want to call the method for each `T` registered in DI
void Method<T>(IService<T> service) {
Type t = typeof(T); // This here will resolve to the actual type, different in each call. Not object or whatever less derived.
}
所有这一切都应该有一些不错的表现。
答案
根据@JeroenMostert的有用评论,我发现了一种方法可以完全按照我的意愿行事。正如他指出的那样,因为我们在编译时不知道泛型参数类型,所以我们不能静态绑定该方法调用。我们需要的是late binding。
反射是一种后期绑定,但有一个更好的解决方案:dynamic
答案的例子将成为:
IEnumerable<IService> services = serviceProvider.GetServices<IService>();
foreach (var s in services) {
Method((dynamic)s);
}
void Method<T>(IService<T> service) {
// This here will resolve to the actual type, different in each call. Not object or whatever less derived.
Type t = typeof(T);
}
当dynamic
的实际类型已知时,对s
的强制转换将推迟方法绑定直到运行时。然后它将寻找最佳拟合重载(如果没有异常将被抛出)。这种方法对使用反射有一些好处:
- 我们不必关心缓存。 DLR(动态语言运行时)将为我们处理它。
- 我们迟到了,但得到尽可能多的静态分析。例如。检查所有其他参数类型,并在无效时导致编译时错误。
- 它更短更容易编写。
您可以阅读关于此方法的优秀深入帖子以及它与反射here的比较。
另一答案
我能想到两个选项:
- 注入
IService
并过滤掉不兼容的类型:serviceProvider.GetServices<IService>().OfType<IService<T>>();
- 重复注册:
services.AddScoped<IService, FooService>(); services.AddScoped<IService, BarService1>(); services.AddScoped<IService, BarService2>(); services.AddScoped<IService<Foo>, FooService>(); services.AddScoped<IService<Bar>, BarService1>(); services.AddScoped<IService<Bar>, BarService2>(); serviceProvider.GetServices<IService<Bar>>(); // returns 2 services serviceProvider.GetServices<IService>(); // returns 3 services
但请注意,您需要注意这些重复注册不要落入Torn Lifestyles陷阱。当服务注册为Scoped
或Singleton
时,可能会发生这种情况。要解决此问题,您需要将以上注册更改为以下内容:services.AddScoped<FooService>(); services.AddScoped<BarService1>(); services.AddScoped<BarService2>(); services.AddScoped<IService>(c => c.GetRequiredService<FooService>()); services.AddScoped<IService>(c => c.GetRequiredService<BarService1>()); services.AddScoped<IService>(c => c.GetRequiredService<BarService2>()); services.AddScoped<IService<Foo>>(c => c.GetRequiredService<FooService>()); services.AddScoped<IService<Bar>>(c => c.GetRequiredService<BarService1>()); services.AddScoped<IService<Bar>>(c => c.GetRequiredService<BarService2>());
此外,由于您似乎在封面下使用不同的容器,您可以使用自动注册(a.k.a。组件扫描)来减少样板。
以上是关于在Microsofts依赖注入中获取开放式通用服务的主要内容,如果未能解决你的问题,请参考以下文章