.Net Core 依赖注入:获取服务类型(接口)的实现类型
.Net Core dependency injection: Get implementationtype(s) for servicetype (interface)
是否可以获取服务类型的注册实现类型?
例如:
在我的 Startup.ConfigureServices
中,我注册了这样的服务(没什么特别的):
public void ConfigureServices(IServiceCollection services)
{
//...
services.AddTransient<IBaseRepository, BaseRepository>();
//...
}
IBaseRepository
是服务类型,BaseRepository
是具体实现。
问题
有没有办法获取服务接口的注册类型(不是实例)?
在我的示例中,我想请求 IBaseRepository
并获取类型 BaseRepository
.
编辑于 10.02.2021 13:13 UTC:以下信息不是问题的一部分,具有误导性。
我的问题的更多背景知识
稍后我需要这个服务的一个实例并想使用
ActivatorUtilities.CreateInstance,因为我需要在仅在运行时才知道的构造函数中传递值。
这里我需要传递 class 的具体类型,我想得到一个新实例。
如果我可以将接口类型传递到我的方法中,确定为此接口注册了哪个 class 并创建一个新实例,那就太好了。
您并不真的需要为您的方案使用 ActivatorUtilities:
检查 net core documentation 中有关 GetRequiredService
的代码,因为我认为它会解决您的问题。
public static void Main(string[] args)
{
var host = CreateHostBuilder(args).Build();
using (var serviceScope = host.Services.CreateScope())
{
var services = serviceScope.ServiceProvider;
try
{
var myDependency = services.GetRequiredService<IMyDependency>();
myDependency.WriteMessage("Call services from main");
}
catch (Exception ex)
{
var logger = services.GetRequiredService<ILogger<Program>>();
logger.LogError(ex, "An error occurred.");
}
}
host.Run();
}
为什么这是首选:
- 您没有在 DI 容器之外的任何地方添加实例化逻辑,因此那里没有副作用
- 在您的情况下,您添加了一个
Transient
范围内的服务,所以应该没问题。如果那是一个单身人士,那么通过激活器创建一个新的单身人士将是一个不好的做法,因为你会违反原作者的意图并产生未知的副作用(好吧,它们可能是已知的,但你明白我的意思)。
仍然不完全清楚你为什么需要这个,但是...... IServiceCollection
(不是内置的IServiceProvider
)只是ServiceDescriptor
个对象。您用于配置 DI 的所有扩展方法本质上都是向集合添加一个或多个描述符。
因为它实现了 IList<ServiceDescriptor>
(因此实现了 IEnumerable<ServiceDescriptor>
),您可以对其执行 LINQ 操作以查找特定的描述符。 ServiceDescriptor
中有四个属性描述了服务及其实现。
ServiceType
- 必需;将从 DI 请求的 Type
。通常但不总是一个接口
ImplementationType
- 可选;已注册的具体 Type
ImplementationInstance
- 可选;单例实例(静态类型object
)
ImplementationFactory
- 可选;用作工厂方法的 Func<IServiceProvider, object>
在三个可选属性中,指定一个(且仅一个)will/must。第五个 属性、Lifetime
描述了生命周期(Singleton、Scoped、Transient)。
如果要查找与特定服务类型关联的具体类型s,可以使用以下扩展方法:
public static IEnumerable<Type> GetImplementationTypes<TService>(this IServiceCollection services)
where TService: class
{
if (services is null)
throw new ArgumentNullException(nameof(services));
return services
.Where(d => d.ServiceType == typeof(TService) && d.ImplementationType != null)
.Select(d => d.ImplementationType);
}
// or alternatively
public static IEnumerable<Type> GetImplementationTypes(this IServiceCollection services, Type serviceType)
{
if (services is null)
throw new ArgumentNullException(nameof(services));
if (serviceType is null)
throw new ArgumentNullException(nameof(serviceType));
return services
.Where(d => d.ServiceType == serviceType && d.ImplementationType != null)
.Select(d => d.ImplementationType);
}
然后你可以使用:
IServiceCollection services =...
var typeList = services.GetImplementationTypes<IBaseRepository>().ToList();
请注意,这 return 是一个 IEnumerable<Type>
,因为您可以为一种服务类型进行多次注册。它也只会 return 那些已经注册了类型的,而不是那些使用工厂或单例实例的。
是否可以获取服务类型的注册实现类型?
例如:
在我的 Startup.ConfigureServices
中,我注册了这样的服务(没什么特别的):
public void ConfigureServices(IServiceCollection services)
{
//...
services.AddTransient<IBaseRepository, BaseRepository>();
//...
}
IBaseRepository
是服务类型,BaseRepository
是具体实现。
问题
有没有办法获取服务接口的注册类型(不是实例)?
在我的示例中,我想请求 IBaseRepository
并获取类型 BaseRepository
.
编辑于 10.02.2021 13:13 UTC:以下信息不是问题的一部分,具有误导性。
我的问题的更多背景知识
稍后我需要这个服务的一个实例并想使用
ActivatorUtilities.CreateInstance,因为我需要在仅在运行时才知道的构造函数中传递值。
这里我需要传递 class 的具体类型,我想得到一个新实例。
如果我可以将接口类型传递到我的方法中,确定为此接口注册了哪个 class 并创建一个新实例,那就太好了。
您并不真的需要为您的方案使用 ActivatorUtilities:
检查 net core documentation 中有关 GetRequiredService
的代码,因为我认为它会解决您的问题。
public static void Main(string[] args)
{
var host = CreateHostBuilder(args).Build();
using (var serviceScope = host.Services.CreateScope())
{
var services = serviceScope.ServiceProvider;
try
{
var myDependency = services.GetRequiredService<IMyDependency>();
myDependency.WriteMessage("Call services from main");
}
catch (Exception ex)
{
var logger = services.GetRequiredService<ILogger<Program>>();
logger.LogError(ex, "An error occurred.");
}
}
host.Run();
}
为什么这是首选:
- 您没有在 DI 容器之外的任何地方添加实例化逻辑,因此那里没有副作用
- 在您的情况下,您添加了一个
Transient
范围内的服务,所以应该没问题。如果那是一个单身人士,那么通过激活器创建一个新的单身人士将是一个不好的做法,因为你会违反原作者的意图并产生未知的副作用(好吧,它们可能是已知的,但你明白我的意思)。
仍然不完全清楚你为什么需要这个,但是...... IServiceCollection
(不是内置的IServiceProvider
)只是ServiceDescriptor
个对象。您用于配置 DI 的所有扩展方法本质上都是向集合添加一个或多个描述符。
因为它实现了 IList<ServiceDescriptor>
(因此实现了 IEnumerable<ServiceDescriptor>
),您可以对其执行 LINQ 操作以查找特定的描述符。 ServiceDescriptor
中有四个属性描述了服务及其实现。
ServiceType
- 必需;将从 DI 请求的Type
。通常但不总是一个接口ImplementationType
- 可选;已注册的具体Type
ImplementationInstance
- 可选;单例实例(静态类型object
)ImplementationFactory
- 可选;用作工厂方法的Func<IServiceProvider, object>
在三个可选属性中,指定一个(且仅一个)will/must。第五个 属性、Lifetime
描述了生命周期(Singleton、Scoped、Transient)。
如果要查找与特定服务类型关联的具体类型s,可以使用以下扩展方法:
public static IEnumerable<Type> GetImplementationTypes<TService>(this IServiceCollection services)
where TService: class
{
if (services is null)
throw new ArgumentNullException(nameof(services));
return services
.Where(d => d.ServiceType == typeof(TService) && d.ImplementationType != null)
.Select(d => d.ImplementationType);
}
// or alternatively
public static IEnumerable<Type> GetImplementationTypes(this IServiceCollection services, Type serviceType)
{
if (services is null)
throw new ArgumentNullException(nameof(services));
if (serviceType is null)
throw new ArgumentNullException(nameof(serviceType));
return services
.Where(d => d.ServiceType == serviceType && d.ImplementationType != null)
.Select(d => d.ImplementationType);
}
然后你可以使用:
IServiceCollection services =...
var typeList = services.GetImplementationTypes<IBaseRepository>().ToList();
请注意,这 return 是一个 IEnumerable<Type>
,因为您可以为一种服务类型进行多次注册。它也只会 return 那些已经注册了类型的,而不是那些使用工厂或单例实例的。