如何将依赖项传递给自定义的.NET Core ILoggerProvider

15

我正在创建一个自定义的.NET Core ILoggerProvider,需要在其构造函数中传递一些依赖项。

我认为我正在使用一种相当常见的模式来初始化我的日志实现;它看起来像这样:

var services = new ServiceCollection();

// Register some services here

services.AddLogging(builder =>
{
    builder.AddProvider(new DebugLoggerProvider());
});

var provider = services.BuildServiceProvider();

我想在AddLogging块中添加我的新提供程序,以与当前添加的DebugLoggerProvider相同的方式。

我的自定义提供程序需要传递一些其他服务到它的构造函数中,由于这些服务已经在ServiceCollection中注册,我认为我应该能够引用它们。然而,与像AddSingleton这样的方法不同,它有一个重载版本,公开了IServiceProviderAddLogging似乎没有类似的选择。

是否有简单的方法可以实现此目标,或者我正在尝试做一些与.NET Core日志记录设计方式相矛盾的事情?

更新:

在尝试@Nkosi提出的建议后,我可以确认可以通过绕过AddLogging并直接实现其内部操作来使其工作,具体如下:

var services = new ServiceCollection();

// Register some services
services.AddSingleton<IMyService, MyService>();

// Initialize logging
services.AddOptions();
services.AddSingleton<ILoggerFactory, LoggerFactory>();
services.AddSingleton(typeof(ILogger<>), typeof(Logger<>));
services.AddSingleton<ILoggerProvider>(p => new DebugLoggerProvider());
services.AddSingleton<ILoggerProvider>(p => new MyLoggerProvider("Constant value", p.GetService<IMyService>()));

var provider = services.BuildServiceProvider();
3个回答

25

我不确定是否已经存在一个扩展来完成这项工作,但我看到了潜力。

首先,这是在源代码存储库中定义 AddProvider 的方式。

public static ILoggingBuilder AddProvider(this ILoggingBuilder builder, ILoggerProvider provider) {
    builder.Services.AddSingleton(provider);
    return builder;
}

你可以通过制作自己的通用版本来进一步完善它。
public static class MyLoggingBuilderExtensions {
    public static ILoggingBuilder AddProvider<T>(this ILoggingBuilder builder)
        where T: class, ILoggerProvider{
        builder.Services.AddSingleton<ILoggerProvider, T>();
        return builder;
    }
}

这应该允许 DI 容器在解析时构建对象图

services.AddLogging(builder =>
{
    builder.AddProvider<CustomLoggerProvider>();
});

这个功能还有扩展的余地,比如添加自己的重载函数来公开IServiceProvider并将其传递给扩展中的AddSingleton

public static ILoggingBuilder AddProvider<T>(this ILoggingBuilder builder, Func<IServiceProvider, T> factory)
    where T: class, ILoggerProvider {
    builder.Services.AddSingleton<ILoggerProvider, T>(factory);
    return builder;
}

并使用

services.AddLogging(builder => {
    builder.AddProvider<CustomLoggerProvider>(p => new CustomLoggerProvider("Constant value", p.GetService<IMyService>()));
});

那是一个天才的解决方案 - 谢谢 :) 正如你所说,它似乎应该已经存在,但是假设没有,我很高兴将你的扩展引入我的代码库。 - Tim Coulter
不幸的是,似乎只有在解决整个依赖链(即创建所有依赖项的实例)之后,才能将提供程序添加到日志记录生成器中。我尝试在AddLogging中复制所有依赖项(及其依赖项)的DI定义(这也很丑),但仍然失败了。看来这个日志记录实现存在一个很大的漏洞。 - Tim Coulter
你说得对,我最终通过展开AddLogging的内部实现并将其内联实现来使其工作,如我帖子中的更新所示。我仍然不清楚为什么使用扩展方法无法实现这一点,但我认为这是一个合理的结果。再次感谢您的帮助。 - Tim Coulter
很棒的解决方案。第二个代码块定义提供了一个小问题:“where T: ILoggerProvider, class” 应该是 “where T: class, ILoggerProvider”(至少 VS 抱怨了一下...) - Mike
基于它们的 GitHub,ILoggerFactory 上的 AddProvider 已被废弃。https://github.com/aspnet/Announcements/issues/238 - Pouya Samie
显示剩余3条评论

0

我找到了一个简单的解决方案,相对比较轻便。

serviceCollection.AddLogging(logBuilder =>
{
    logBuilder.AddConfiguration(theConfigRoot.GetSection("Logging"));
});
serviceCollection.AddSingleton<ILoggerProvider, MyLogProvider>();

然而,实例化提供程序可以避免您遇到循环依赖问题——您可能很快想要注入的服务本身需要一个记录器^^


0

很抱歉这个问题我来晚了,但是我也曾经四处寻找答案却遇到了同样的问题。在参考了这个页面上的优秀解决方案后,我最终得出了以下解决方案。

services.AddTransient<IMyLogRepository, LogRepository>();
var loggerFactory = LoggerFactory.Create(builder =>
        {
            builder.AddConsole()
                   .AddDbLoggerProvider(services.BuildServiceProvider().GetService<IMyLogRepository>());
        });
        services.AddSingleton(loggerFactory.CreateLogger("MyLogging"));

关键在于:

services.BuildServiceProvider().GetService<IMyLogRepository>())

这让我能够通过一行额外的代码将我的数据库仓库与我创建的dbLogger对象链接起来。本质上,它使我能够通过标准的ILoggerProvider和ILogger接口从我的DI数据库对象中提取并发送到日志记录服务中。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接