You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自定义Logger依赖服务时如何获取同一单例实例?

解决自定义Logger依赖服务重复实例化的问题

问题出在你提前手动构建了ServiceProvider——在ConfigureServices里调用services.BuildServiceProvider()会创建一个独立于最终Host容器的服务实例池,你传给自定义Logger的IMycustomService来自这个临时容器,而应用其他组件使用的是Host最终构建的容器里的实例,哪怕服务注册为单例,两个容器的实例也是完全隔离的,自然会出现重复创建的情况。

正确实现方式

不要手动传递服务实例,让自定义Logger的Provider通过依赖注入从Host的主容器中获取服务,步骤如下:

  1. 移除提前构建ServiceProvider的代码
    删掉ConfigureServices里的serviceProvider = services.BuildServiceProvider();,所有服务统一由Host的主容器管理。

  2. 修改自定义Logger的扩展方法
    让AddCustomFormatter直接注册你的ILoggerProvider,而不是接收服务实例:

    public static class LoggingBuilderExtensions
    {
        public static ILoggingBuilder AddCustomFormatter(this ILoggingBuilder builder)
        {
            // 将LoggerProvider注册为单例(符合.NET日志系统要求)
            builder.Services.AddSingleton<ILoggerProvider, CustomFormatterLoggerProvider>();
            return builder;
        }
    }
    
  3. 让LoggerProvider通过构造函数注入依赖
    自定义的ILoggerProvider实现类,通过构造函数接收IMycustomService,这样会自动从Host的主容器中解析单例实例:

    public class CustomFormatterLoggerProvider : ILoggerProvider
    {
        private readonly IMycustomService _customService;
    
        public CustomFormatterLoggerProvider(IMycustomService customService)
        {
            _customService = customService;
        }
    
        public ILogger CreateLogger(string categoryName)
        {
            // 传入注入的服务实例创建Logger
            return new CustomFormatterLogger(_customService);
        }
    
        public void Dispose()
        {
            // 按需清理资源
        }
    }
    
  4. 调整Host构建代码
    调用修改后的AddCustomFormatter方法,不再手动传递服务实例:

    return Host.CreateDefaultBuilder(args)                
        .ConfigureHostConfiguration(configurationBuilder =>
        {
            if (args is not null)
            {
                _ = configurationBuilder.AddCommandLine(args);
            }
        })
        .ConfigureAppConfiguration((context, builder) =>
            builder.SetBasePath(AppContext.BaseDirectory)
                .AddJsonFile("appsettings.json", optional: true))                
        .ConfigureServices((hostContext, services) =>
        {
            services.AddServices();
            // 移除提前BuildServiceProvider的代码
        })
        .ConfigureLogging((hostContext, loggingBuilder) =>
        {
            loggingBuilder
                .AddConsole()
                .AddEventLog()                            
                .AddFile("Logs/worker-{Date}.txt")
                .AddCustomFormatter(); // 直接调用扩展方法
        });
    

核心原理

  • Host最终只会构建一个全局的ServiceProvider,所有注册的单例服务都会在这个容器中只实例化一次。
  • ILoggerProvider被注册为单例后,会在日志系统初始化时从主容器解析,其构造函数中的依赖也会从同一个容器获取,确保实例唯一。

关键注意事项

  • 绝对不要在ConfigureServices阶段提前调用BuildServiceProvider(),这会破坏容器的统一性,引发实例重复、配置不一致等问题。
  • 所有ILoggerProvider实现都必须注册为单例,这是.NET日志框架的强制要求,否则会导致Provider被频繁创建,进而引发依赖服务的重复实例化。

内容的提问来源于stack exchange,提问作者Theo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 07:05:32