虽然更成熟且功能丰富的 DI 容器(例如 Autofac 和 Simple Injector)确实支持基于上下文的注入(这是您正在寻找的功能),但 MS.DI 不支持此功能。
正是出于这个原因,所有 Microsoft 文档都描述了泛型 ILogger<T> 的注入,其中 T 等于消费类型,如下所示:
public class C
{
public C(ILogger<C> logger) { ... }
}
然而,让消费者依赖于泛型 ILogger<T>,而不是简单地依赖于非泛型 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<T> 依赖。
限制:
- 不适用于开放通用注册
- 不适用于代表注册
- 用另一个 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。