.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();
    }

为什么这是首选:

  1. 您没有在 DI 容器之外的任何地方添加实例化逻辑,因此那里没有副作用
  2. 在您的情况下,您添加了一个 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 那些已经注册了类型的,而不是那些使用工厂或单例实例的。