【问题标题】:.Net Core dependency injection: Get implementationtype(s) for servicetype (interface).Net Core 依赖注入:获取服务类型(接口)的实现类型
【发布时间】:2021-02-14 07:51:19
【问题描述】:

是否可以获得服务类型的注册实现的类型?

例如: 在我的Startup.ConfigureServices 中,我注册了一个这样的服务(没什么特别的):

public void ConfigureServices(IServiceCollection services)
{
  //...
  services.AddTransient<IBaseRepository, BaseRepository>();
  //...
}

IBaseRepository 是服务类型,BaseRepository 是具体实现。

问题

有没有办法获取服务接口注册的type(不是实例)? 在我的示例中,我想询问 IBaseRepository 并获取类型 BaseRepository

在 2021 年 2 月 10 日 13:13 UTC 编辑:以下信息不是问题的一部分,具有误导性。 我的问题的更多背景

稍后我需要这个服务的一个实例,并希望使用它来创建它 ActivatorUtilities.CreateInstance,因为我需要在仅在运行时知道的构造函数中传递值。 在这里,我需要为要获取新实例的类传递具体类型。 如果我可以将接口类型传递给我的方法,确定为该接口注册了哪个类并创建一个新实例,那就太好了。

【问题讨论】:

  • 这似乎是XY problem。根据背景故事,当前状态的问题也不完整,因此不清楚。阅读How to Ask,然后编辑问题以提供可用于重现问题的minimal reproducible example,从而更好地了解实际询问的内容。
  • 如果您有IServiceCollection不是内置的IServiceProvider),您可以使用LINQ方法对其进行操作并找到descriptor.ServiceType==X的注册(复数!)并选择descriptor.ImplementationType,但如果有任何在工厂或预制实例中注册,您就会遇到问题。但我还是不知道为什么你会想要这个
  • @pinkfloydx33:谢谢。这就是我一直在寻找的。我花了几个小时尝试并寻找一种从构建IServiceProvider 中获取信息的方法。并感谢工厂的提示。帮助我关注它。 @Nkosi:我的问题只是:是否可以获得服务类型的注册实现的类型?。您认为如果我不添加“我的问题的更多背景信息”部分会更清楚吗?
  • @BooFar 是的,背景添加了额外的细节,将问题扩大到稀释了您实际寻找的内容的程度。

标签: c# .net-core dependency-injection


【解决方案1】:

仍然不完全清楚为什么需要这个,但是...IServiceCollection不是内置的IServiceProvider)只是ServiceDescriptor 对象的集合。用于配置 DI 的所有扩展方法本质上都是向集合中添加一个或多个描述符。

因为它实现了IList&lt;ServiceDescriptor&gt;(因此实现了IEnumerable&lt;ServiceDescriptor&gt;),所以您可以对它执行LINQ 操作以查找特定的描述符。 ServiceDescriptor 上有四个值得注意的属性,用于描述服务及其实现。

  • ServiceType - 必填;将向 DI 请求的 Type。通常但不总是一个接口
  • ImplementationType - 可选;已注册的具体Type
  • ImplementationInstance - 可选;一个单例实例(静态类型object
  • ImplementationFactory - 可选; Func&lt;IServiceProvider, object&gt; 用作工厂方法

在三个可选属性中,将/必须指定一个(并且只有一个)。第五个属性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();

请注意,这将返回 IEnumerable&lt;Type&gt;,因为您可以为单个服务类型进行多次注册。它也只会返回那些已经注册了一个类型的,而不是那些使用工厂或单例实例的。

【讨论】:

    【解决方案2】:

    您实际上并不需要为您的场景使用 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 范围服务,所以应该没问题。如果那是一个单例,那么由激活器创建一个新的单例将是一个不好的做法,因为您会违反原作者的意图并产生未知的副作用(好吧,它们可能是已知的,但您明白我的意思)。李>

    【讨论】:

    • 您好 Athanasios,感谢您的回答。但是GetRequiredService 将如何帮助将参数传递给未在 DI 容器中注册的服务的构造函数?
    • 您将传递参数并在类中使用 GetRequiredService。还是我误解了什么?
    • 抱歉,我了解到我的问题并不清楚。毫无疑问如何使用ActivatorUtilities.CreateInstance。问题是关于如何获取为服务类型注册了哪些实施的信息。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-11-20
    • 2019-04-13
    • 1970-01-01
    • 2016-11-21
    • 2019-09-06
    • 1970-01-01
    • 2019-03-22
    相关资源
    最近更新 更多