自定义Logger依赖服务时如何获取同一单例实例?
解决自定义Logger依赖服务重复实例化的问题
问题出在你提前手动构建了ServiceProvider——在ConfigureServices里调用services.BuildServiceProvider()会创建一个独立于最终Host容器的服务实例池,你传给自定义Logger的IMycustomService来自这个临时容器,而应用其他组件使用的是Host最终构建的容器里的实例,哪怕服务注册为单例,两个容器的实例也是完全隔离的,自然会出现重复创建的情况。
正确实现方式
不要手动传递服务实例,让自定义Logger的Provider通过依赖注入从Host的主容器中获取服务,步骤如下:
移除提前构建ServiceProvider的代码
删掉ConfigureServices里的serviceProvider = services.BuildServiceProvider();,所有服务统一由Host的主容器管理。修改自定义Logger的扩展方法
让AddCustomFormatter直接注册你的ILoggerProvider,而不是接收服务实例:public static class LoggingBuilderExtensions { public static ILoggingBuilder AddCustomFormatter(this ILoggingBuilder builder) { // 将LoggerProvider注册为单例(符合.NET日志系统要求) builder.Services.AddSingleton<ILoggerProvider, CustomFormatterLoggerProvider>(); return builder; } }让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() { // 按需清理资源 } }调整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
相关产品推荐
相关产品推荐

