在我的定制ConsoleFormatter中,我需要从添加到DI的服务中获取一个值.该值保存在作用域服务中,因为它 for each 新请求生成一个新值,但应该在请求期间保留该值.

问题是我不知道如何访问ConsoleFormatter中的DI服务,我在下面try 的是将服务容器放入选项参数中.我在这里的 idea 是.NET服务是一个单例,这样我就可以在登录时请求我的服务并获得我的作用域服务.

public static class ConsoleLoggerExtensions
{
    public static ILoggingBuilder AddCustomConsoleFormatter(
    this ILoggingBuilder builder, Action<CustomConsoleFormatterOptions> options)
    {
        return builder.AddConsole(
            options => options.FormatterName = nameof(CustomLoggingFormatter))
            .AddConsoleFormatter<
                CustomLoggingFormatter, CustomConsoleFormatterOptions>(
                options);
    }
}

public sealed class CustomConsoleFormatterOptions : ConsoleFormatterOptions
{
    public IServiceProvider serviceProvider { get; set; }
}

public sealed class CustomLoggingFormatter : ConsoleFormatter, IDisposable
{
    static long RowIndex = 1;
    readonly IDisposable _optionsReloadToken;
    CustomConsoleFormatterOptions _formatterOptions;

    public CustomLoggingFormatter(
        IOptionsMonitor<CustomConsoleFormatterOptions> options)
        : base(nameof(SebLoggingFormatter))
    {
        (_optionsReloadToken, _formatterOptions) =
            (options.OnChange(ReloadLoggerOptions), options.CurrentValue);
    }

    public override void Write<TState>(in LogEntry<TState> logEntry,
        IExternalScopeProvider scopeProvider, TextWriter textWriter)
    {
        string message =
            logEntry.Formatter?.Invoke(logEntry.State, logEntry.Exception);
        if (message is null)
            return;

        var idService =
            _formatterOptions.serviceProvider.GetService<IIdService>();
        var requestId = idService.RequestId;
    }

    void ReloadLoggerOptions(CustomConsoleFormatterOptions options) =>
        _formatterOptions = options;

    public void Dispose() =>
        _optionsReloadToken?.Dispose();
}

在我的初创公司中,我添加了一个任务来获取CustomFormnaytterOptions

services.AddLogging(opt => opt.AddCustomConsoleFormatter(options =>
{
    var serviceProvider = services.BuildServiceProvider();
    options.serviceProvider = serviceProvider;
}));

但是,当我从服务请求IdService时,我得到的是一个空值的服务,对我来说,这可能是因为:

  • 无Http上下文(IdService正在通过当前请求中的标头拾取或创建ID)
  • 我在这里使用的.Net服务是启动时的旧服务(=不是单例),因此与当前请求无关

我不理解的一件事是,(ConsoleFormatterOptions使用的).Net中的选项编码模式似乎是一个内置的功能.我应该以某种方式try 使选项无效,以便它从启动时重新运行任务吗?

推荐答案

您的解决方案中有几个问题阻碍了它的工作:

  • CustomConsoleFormatterOptions.serviceProvider字段设置为services.BuildServiceProvider()将使格式化程序获得所有DI注册的副本.这不会让您访问请求IIdService,也不会让您访问活动作用域或HTTP请求.
  • 多次调用BuildServiceProvider等于problematic practice,这就是为什么.NET Core中有一个代码分析器会就此向您发出警告.
  • 框架始终假定日志(log)记录器及其格式化程序是单例的.尽管格式化程序可以注入依赖项,但这些依赖项本身是、应该是或将成为单例.如果你注入IServiceProvider,它将成为"根"服务提供者,并且不会给你访问范围服务的权限.

解决方案是注入IHttpContextAccessor类,因为它允许访问当前的HttpContext:

public sealed class CustomLoggingFormatter : ConsoleFormatter
{
    private readonly IHttpContextAccessor accessor;

    public CustomLoggingFormatter(IHttpContextAccessor accessor)
        : base(nameof(SebLoggingFormatter))
    {
        this.accessor = accessor;
    }

    public override void Write<TState>(in LogEntry<TState> logEntry,
        IExternalScopeProvider scopeProvider, TextWriter textWriter)
    {
        string message =
            logEntry.Formatter?.Invoke(logEntry.State, logEntry.Exception);
        if (message is null)
            return;

        // Pull scoped service from request
        var idService =
            this.accessor.HttpContext.RequestServices.GetService<IIdService>();

        var requestId = idService.RequestId;
    }
}

它可以按如下方式连接:

builder.AddHttpAccessor();

builder.AddConsole(
    options => options.FormatterName = nameof(CustomLoggingFormatter))
    .AddConsoleFormatter<
        CustomLoggingFormatter, CustomConsoleFormatterOptions>();

Csharp相关问答推荐

如何循环遍历XML文档 node 以使用XSLT存储值

TDLib与机器人共享电话号码

. NET在上一个操作完成之前,在此上下文实例上启动了第二个操作

如何在不考虑年份的情况下判断日期时间是否在某个日期范围内?

在实体框架中处理通用实体&S变更跟踪器

我想在文本框悬停时在其底部显示一条线

ASP.NET配置kestrel以使用Windows证书存储中的HTTPS

如何使用EF Core和.NET 8来upsert到具有多对多关系的表?

C#普罗米修斯指标

EF核心区分大小写的主键

try 链接被委派者(多播委托)时,无法将获取运算符应用于类型为';方法组&39;和方法组';的操作数

基于C#和ANGING的SignalR实时聊天流媒体应用

Blazor:搜索框在第一次搜索时不搜索

如何在C#中正确类型化带有泛型的嵌套类

如何设置WinForms按钮焦点,使其看起来像是被Tab键插入其中?

WPF:如何从DatagridHeader的内容模板绑定到词典项

实例化列表时的集合表达式是什么?

这是T自身的布尔表达式是什么意思?

MudRadioGroup不切换

在SQL中删除少于24小时的令牌