ASP.NET Core DI 框架 - 如何根据调用者类解析一个类

问题描述 投票:0回答:1

我们正在将旧的 .Net 框架应用程序迁移到 Net 8。我们有自己的 ILogger 接口和我们自己的 Logger 实现,并且我们在数千个类中使用自定义 ILogger。现在我们想用 Microsoft.ILogger“替换”它,并利用默认的 .Net DI 容器。

我们所做的第一步是围绕 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>。任何帮助将不胜感激。

尝试过谷歌搜索,但找不到任何有用的东西。

asp.net-core logging dependency-injection
1个回答
0
投票

详细说明我的评论,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>

但这只是尝试此技术时会遇到的许多复杂情况的一个例子。

© www.soinside.com 2019 - 2024. All rights reserved.