【问题标题】:How do I get Type that Dependency is being injected into?如何获得依赖注入的类型?
【发布时间】:2020-06-18 14:04:28
【问题描述】:

我正在使用 .net core 2.2 并使用依赖注入。我有一些自定义的基础架构依赖项来管理我注入到类中的日志记录、跟踪等。我希望依赖项知道它们所在的类。如果我实例化像 new Logger<Type>(); 这样的类,我可以做到这一点,但是可以通过依赖注入来管理吗?

例如:

public class Logger<Type> : ILogger
{

}

public class Foo
{
    private Ilogger _logger;

    public Foo(ILogger logger)
    {
       _logger = logger;
    }

    public void DoStuff()
    {
        _logger.Log();  //<= knows it's in the Foo class.
    }
}

如何将 ILogger 注入到 Foo 中,并让 Logger 知道它被注入了什么类型?

【问题讨论】:

  • 为什么不提供类型作为.Log 方法的参数?
  • 所以我目前正在这样做作为一种解决方法,我正在寻找更优雅的东西。我敢肯定,这不是边缘情况,并且 dotnet 将有一个需要注入泛型类的答案。

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


【解决方案1】:

你可以像这样指定类型

public class Foo
{
    private Ilogger<Foo> _logger;
}

然后在Logger中,就可以知道T的类型了

public class Logger<T> : ILogger<T>
{
    public Type WhatType() => typeof(T);
}

您需要像这样注册服务

serviceCollection.AddSingleton(typeof(ILogger<>), typeof(Logger<>));

【讨论】:

    【解决方案2】:

    虽然更成熟且功能丰富的 DI 容器(例如 AutofacSimple Injector)确实支持基于上下文的注入(这是您正在寻找的功能),但 MS.DI 不支持此功能。

    正是出于这个原因,所有 Microsoft 文档都描述了泛型 ILogger&lt;T&gt; 的注入,其中 T 等于消费类型,如下所示:

    public class C
    {
        public C(ILogger<C> logger) { ... }
    }
    

    然而,让消费者依赖于泛型 ILogger&lt;T&gt;,而不是简单地依赖于非泛型 ILogger,会更加冗长且容易出错。它使您的代码更难测试和维护。这可能是您尝试注入上下文感知 ILogger 的原因,为此我为您鼓掌。

    您可以通过迭代ServiceCollection 将此功能“破解”到 MS.DI 中,但实际上它存在太多限制,无法在您的生产应用程序中使用。但是,为了娱乐和娱乐,您可以尝试以下方法:

    // Run just before finalizing the IServiceCollection
    for (int i = 0; i < services.Count; i++)
    {
        ServiceDescriptor descriptor = services[i];
    
        if (descriptor.ImplementationType != null)
        {
            var loggerParameters =
                from ctor in descriptor.ImplementationType.GetConstructors()
                from param in ctor.GetParameters()
                where param.ParameterType == typeof(ILogger)
                select param;
    
            if (loggerParameters.Any())
            {
                // Replace registration
                services[i] =
                    new ServiceDescriptor(
                        descriptor.ServiceType,
                        provider =>
                            ActivatorUtilities.CreateInstance(
                                provider,
                                descriptor.ImplementationType,
                                provider.GetRequiredService(
                                    typeof(ILogger<>).MakeGenericType(
                                        descriptor.ImplementationType))),
                        descriptor.Lifetime);
            }
        }
    }
    

    这段代码的作用是:

    • 搜索注册
      • (封闭或非泛型)实现类型
      • 使用自动连线(因此无需 lambda 注册或实例注册)
      • 其构造函数依赖于ILogger
    • 并将该注册替换为自动关联类型的 lambda 注册(使用 ActivatorUtilities),同时将 ILogger 依赖替换为 ILogger&lt;T&gt; 依赖。

    限制:

    • 不适用于开放通用注册
    • 不适用于代表注册
    • 用另一个 DI 容器替换 MS.DI 时可能无法正常工作(例如,可能会“屏蔽”容器)。
    • 使用多个构造函数时行为未定义

    因此,在实践中,我不建议使用这种方法,而是使用成熟的 DI Container。

    提示:为了获得一些灵感,在使用 Simple Injector 时,可以按如下方式进行这种基于上下文的注册:

    container.RegisterConditional(
        typeof(ILogger),
        c => typeof(Logger<>).MakeGenericType(c.Consumer.ImplementationType),
        Lifestyle.Singleton,
        c => true);
    

    当与 ASP.NET Core 集成时,Simple Injector 甚至包含一个扩展方法来简化这一点。 Here 的 Simple Injector 文档,介绍了如何配置容器以注入 ASP.NET Core 的 ILogger,有关如何在 Simple Injector 中应用基于上下文的注入的基本描述可以找到 here

    【讨论】:

    • 添加了一个如何在 MS.DI 中使用 ILogger 依赖项的示例(不要在工作中尝试这个)。
    猜你喜欢
    • 1970-01-01
    • 2013-08-23
    • 2018-06-29
    • 2016-03-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-02
    • 1970-01-01
    相关资源
    最近更新 更多