Я пытаюсь создать собственный приемник SeriLog, который связан с EntityFrameworkCore.Я нашел существующий файл с именем Serilog.Sinks.EntityFrameworkCore , но он использовал свой собственный DbContext, и мне нужно иметь возможность использовать существующий DbContext.
Итак, я в основном создал свою собственную версиюкод, который работает с моим DbContext.Тем не менее, каждый раз, когда вызывается метод Emit и он пытается загрузить DbContext, я получаю следующую ошибку:
Cannot resolve scoped service ... from root provider
Я видел другие сообщения по этой проблеме, которые связаны с областями обслуживанияи промежуточное ПО.Однако я не верю, что у меня есть промежуточное программное обеспечение.
В двух словах, вот основные части моего кода (опять же, большая часть которых скопирована из ранее упомянутого Git Repo).
startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<EligibilityDbContext>(opts => opts.UseSqlServer(Configuration.GetConnectionString("EligibilityDbConnection")));
}
public void Configure(IApplicationBuilder app,
IHostingEnvironment env,
SystemModelBuilder modelBuilder,
ILoggerFactory loggerFactory)
{
Log.Logger = new LoggerConfiguration()
.WriteTo.EntityFrameworkSink(app.ApplicationServices.GetService<EligibilityDbContext>)
.CreateLogger();
loggerFactory.AddSeriLog();
}
EntityFrameworkSinkExtensions.cs
public static class EntityFrameworkSinkExtensions
{
public static LoggerConfiguration EntityFrameworkSink(
this LoggerSinkConfiguration loggerConfiguration,
Func<EligibilityDbContext> dbContextProvider,
IFormatProvider formatProvider = null)
{
return loggerConfiguration.Sink(new EntityFrameworkSink(dbContextProvider, formatProvider));
}
}
EntityFrameworkSink.cs
public class EntityFrameworkSink : ILogEventSink
{
private readonly IFormatProvider _formatProvider;
private readonly Func<EligibilityDbContext> _dbContextProvider;
private readonly JsonFormatter _jsonFormatter;
static readonly object _lock = new object();
public EntityFrameworkSink(Func<EligibilityDbContext> dbContextProvider, IFormatProvider formatProvider)
{
_formatProvider = formatProvider;
_dbContextProvider = dbContextProvider ?? throw new ArgumentNullException(nameof(dbContextProvider));
_jsonFormatter = new JsonFormatter(formatProvider: formatProvider);
}
public void Emit(LogEvent logEvent)
{
lock (_lock)
{
if (logEvent == null)
{
return;
}
try
{
var record = ConvertLogEventToLogRecord(logEvent);
//! This is the line causing the problems!
DbContext context = _dbContextProvider.Invoke();
if (context != null)
{
context.Set<LogRecord>().Add(this.ConvertLogEventToLogRecord(logEvent));
context.SaveChanges();
}
}
catch(Exception ex)
{
// ignored
}
}
}
private LogRecord ConvertLogEventToLogRecord(LogEvent logEvent)
{
if (logEvent == null)
return null;
string json = this.ConvertLogEventToJson(logEvent);
JObject jObject = JObject.Parse(json);
JToken properties = jObject["Properties"];
return new LogRecord
{
Exception = logEvent.Exception?.ToString(),
Level = logEvent.Level.ToString(),
LogEvent = json,
Message = logEvent.RenderMessage(this._formatProvider),
MessageTemplate = logEvent.MessageTemplate?.ToString(),
TimeStamp = logEvent.Timestamp.DateTime.ToUniversalTime(),
EventId = (int?)properties["EventId"]?["Id"],
SourceContext = (string)properties["SourceContext"],
ActionId = (string)properties["ActionId"],
ActionName = (string)properties["ActionName"],
RequestId = (string)properties["RequestId"],
RequestPath = (string)properties["RequestPath"]
};
}
private string ConvertLogEventToJson(LogEvent logEvent)
{
if (logEvent == null)
{
return null;
}
StringBuilder sb = new StringBuilder();
using (StringWriter writer = new StringWriter(sb))
{
this._jsonFormatter.Format(logEvent, writer);
}
return sb.ToString();
}
}
Ошибка возникает в EntityFrameworkSink.cs на линии DbContext context = _dbContextProvider.Invoke();
Есть мысли о том, почему это вызывает ошибку и как это работает?
Обновление
На основании комментариев Эрика я обновил свой код startup.cs следующим образом:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, SystemModelBuilder modelBuilder, ILoggerFactory loggerFactory, IServiceProvider provider)
{
Log.Logger = new LoggerConfiguration()
.WriteTo.EntityFrameworkSink(provider.GetService<EligibilityDbContext>)
.CreateLogger();
}
Теперь я получаю сообщение об ошибке: Cannot access a disposed object. Object name: IServiceProvider
Предостережение для ответа
Поэтому я отметил ответ Тао Чжоу как ответ.Тем не менее, не то, что он сказал, а код, который он предоставил, на самом деле дал ответ.Я не верю, что EmitBatchAsync
на самом деле решит мою проблему, однако я наткнулся на пару других комментариев и т. Д., Которые указывают, что это может помочь повысить производительность.
Что на самом делеРешил проблему, следуя его примеру кода.При запуске он передает app.ApplicationServices
.Затем в реальной реализации Sink он создал область для разрешения экземпляра dbContext:
using(var context = service.CreateScope().ServiceProvider.GetRequiredService<EligibilityDbContext>())
{
}
Это фактически разрешило все ошибки, которые я получал, и получилось так, как я ожидал.Спасибо