我们正在将旧的 .Net 框架应用程序迁移到 Net 8。我们有自己的 ILogger 接口和我们自己的 Logger 实现,并且我们在数千个类中使用自定义 ILogger。现在我们想用 Microsoft.ILogger
我们所做的第一步是围绕 Microsoft.Extensions.Logging.ILogger 创建一个包装器。
public class NewLoggerWrapper<T> : Custom.ILogger
{
private readonly Microsoft.Extensions.Logging.ILogger _logger;
public NewLoggerWrapper(Microsoft.Extensions.Logging.ILogger<T> logger)
{
_logger = logger;
}
//Implements all Custom.ILogger methods that calling the corresponding Microsoft.ILogger methods
}
现在的重点是如何在服务集合中注册 Custom.ILogger,使其指向通用的 NewLoggerWrapper
public class Repository
{
public Repository(Custom.ILogger logger)
{
}
}
当 Repository->Custom.ILogger 被解析时,我们希望调用 NewLoggerWrapper<Repository>。我们可以使用预定义的类实例化 NewLoggerWrapper,例如 NewLoggerWrapper<Application>,但不知道如何为 <SpecificGenericGeneric 类实例化 NewLoggerWrapperSpecificGeneric>。任何帮助将不胜感激。
尝试过谷歌搜索,但找不到任何有用的东西。
详细说明我的评论,MS.DI 不支持基于上下文的注入,就像其他一些 DI 容器一样。 MS.DI 的功能集故意最小化,试图使其成为最小公分母。
正如我在评论中提到的,有一个解决方法。解决方法包括迭代
ServiceDescriptors
列表并替换其中包含 Custom.ILogger
作为依赖项的任何一个。下面的代码演示了这一点:
services.AddLogging();
services.AddSingleton(typeof(NewLoggerWrapper<>));
// This code must run after all registrations have been made.
for (int i = 0; i < services.Count; i++)
{
ServiceDescriptor descriptor = services[i];
// For simplicity, this example skips keyed registrations.
if (descriptor.IsKeyedService) continue;
// It's impossible to handle factory registrations.
if (descriptor.ImplementationType is null) continue;
var ctors = descriptor.ImplementationType.GetConstructors();
// For simplicity, this example only handles types with a single public ctors
if (ctors.Length != 1) continue;
// Check if the constructor contains the custom ILogger
if (!ctors[0].GetParameters().Any(p => p.ParameterType == typeof(Custom.ILogger)))
continue;
Type logger = typeof(NewLoggerWrapper<>).MakeGenericType(descriptor.ImplementationType);
// Replace the registration with a factory registration that injects
// the NewLoggerWrapper<{ImplementationType}>.
services[1] = new ServiceDescriptor(
serviceType: descriptor.ServiceType,
lifetime: descriptor.Lifetime,
factory: sp => ActivatorUtilities.CreateInstance(
provider: sp,
instanceType: descriptor.ImplementationType,
parameters: new[] { sp.GetRequiredService(logger) }));
}
代码中提到了这个例子的一些缺点。然而,代码中提到的缺点可以通过一些努力来解决。不幸的是,上述方法不适用于工厂注册(例如
.AddTransient<IService>(sp => MyService(sp.GetService<ILogger>()))
,并且此限制无法修复。
如果 MS.DI 的功能更丰富,这个限制就不会那么糟糕。不幸的是,由于功能集有限,许多更高级的功能需要通过使用工厂注册来实现。 Scrutor 等库为 MS.DI 添加了装饰器支持。但它使用与上面代码相同的方法,即用工厂注册替换ImplementationType注册。 (旁注:微软的
AddHttpClient
扩展方法采用了类似的方法)
但这意味着 Scrutor 的应用装饰器不会注入你的
NewLoggerWrapper<T>
。
但这只是尝试此技术时会遇到的许多复杂情况的一个例子。