在Microsofts依赖注入中获取开放式通用服务

问题描述 投票:1回答:2

假设我们有以下服务:

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.
}

所有这一切都应该有一些不错的表现。

c# generics asp.net-core dependency-injection
2个回答
1
投票

根据@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的比较。


0
投票

我能想到两个选项:

  1. 注入IService并过滤掉不兼容的类型: serviceProvider.GetServices<IService>().OfType<IService<T>>();
  2. 重复注册: 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陷阱。当服务注册为ScopedSingleton时,可能会发生这种情况。要解决此问题,您需要将以上注册更改为以下内容: 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。组件扫描)来减少样板。

© www.soinside.com 2019 - 2024. All rights reserved.