英文:
How to use db context in custom logger provider .NET 6
问题
我无法将数据库上下文添加到Program.cs中的自定义日志记录提供程序中。
我尝试以下代码:
builder.Services.AddScoped<EFDataContext>();
var app = builder.Build();
builder.Logging.AddProvider(new CustomLoggerProvider(app.Services.GetRequiredService<EFDataContext>()));
但是出现错误(无法使用单例模式进行dbContext,因为存在选项):'无法从根提供程序解析作用域服务 'eSchronisko.Server.Domain.EFDataContext'。'。
英文:
I can`t add database context into custom logger provider in Program.cs
I try code below:
builder.Services.AddScoped<EFDataContext>();
var app = builder.Build();
builder.Logging.AddProvider(new CustomLoggerProvider(app.Services.GetRequiredService<EFDataContext>()));
but get an error (and can`t use singleton for dbContext due to options): 'Cannot resolve scoped service 'eSchronisko.Server.Domain.EFDataContext' from root provider.'.
答案1
得分: 1
-
我建议不要重新发明轮子,而是使用已存在的库,该库提供了将日志记录到数据库的功能。例如,Serilog,它有多个适配器允许将日志写入不同的数据库。
-
如果你仍然考虑重新发明轮子出于某种原因 - 最好查看扩展一些现有的日志记录库,而不是从头开始。
-
如果你不想在现有库的基础上构建 - 我强烈建议防止多次构建应用程序,并通过以下方法利用日志记录器提供程序中的依赖注入(参见自定义日志记录器文档):
public class CustomLoggerProvider : ILoggerProvider { private readonly IServiceScopeFactory _scopeFactory; public CustomLoggerProvider(IServiceScopeFactory scopeFactory) { _scopeFactory = scopeFactory; } public void Dispose(){} // 待办事项 public ILogger CreateLogger(string categoryName) => new CustomLogger(categoryName, _scopeFactory); } public sealed class CustomLogger : ILogger { private readonly IServiceScopeFactory _scopeFactory; public CustomLogger(string category, IServiceScopeFactory scopeFactory) { _scopeFactory = scopeFactory; } public IDisposable? BeginScope<TState>(TState state) where TState : notnull => default!; public bool IsEnabled(LogLevel logLevel) => true; // 待办事项 public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter) { using var serviceScope = _scopeFactory.CreateScope(); var provider = serviceScope.ServiceProvider; // 解析作用域服务,例如数据库上下文: var someService = provider.GetRequiredService<SomeService>(); // 使用 someService... } }
并进行注册:
builder.Services.TryAddEnumerable(new ServiceDescriptor( typeof(ILoggerProvider), typeof(CustomLoggerProvider), ServiceLifetime.Singleton ));
-
解决初始问题 - 要从根作用域访问上下文,你需要更改上下文的生命周期范围(默认为 Scoped,尽管个人在一般情况下不建议更改它),或者手动创建一个作用域 - 请参阅此答案。
英文:
-
I would recommend to skip reinventing the wheel and use an existing library which provides logging into database. For example Serilog which has several sinks allowing to write to different databases.
-
If you still considering reinventing the wheel for some reason - still better look into extending some existing logger library then starting from scratch.
-
If you do not want to build on top of the existing library - I strongly recommend prevent building app several times and leverage DI in the logger provider via the following approach (see the custom logger docs):
public class CustomLoggerProvider : ILoggerProvider { private readonly IServiceScopeFactory _scopeFactory; public CustomLoggerProvider(IServiceScopeFactory scopeFactory) { _scopeFactory = scopeFactory; } public void Dispose(){} // todo public ILogger CreateLogger(string categoryName) => new CustomLogger(categoryName, _scopeFactory); } public sealed class CustomLogger : ILogger { private readonly IServiceScopeFactory _scopeFactory; public CustomLogger(string category, IServiceScopeFactory scopeFactory) { _scopeFactory = scopeFactory; } public IDisposable? BeginScope<TState>(TState state) where TState : notnull => default!; public bool IsEnabled(LogLevel logLevel) => true; // todo public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter) { using var serviceScope = _scopeFactory.CreateScope(); var provider = serviceScope.ServiceProvider; // resolve scoped service, for example db context: var someService = provider.GetRequiredService<SomeService>(); // use someService... } }
and registration:
builder.Services.TryAddEnumerable(new ServiceDescriptor( typeof(ILoggerProvider), typeof(CustomLoggerProvider), ServiceLifetime.Singleton ));
-
Addressing the initial problem - to access context from the root scope you need either change the context lifetime scope (default - Scoped, though personally I would not recommend changing it in general case) or create a scope manually - see this answer.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论