Правильное использование введенной зависимости DbContext для запроса базы данных

#asp.net #entity-framework #dependency-injection #.net-core

#asp.net #entity-framework #внедрение зависимостей #.net-core

Вопрос:

Я настроил свой собственный контекст, используя (как я считаю правильным):

 public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContextPool<JobTrackingContext>(options => options.UseNpgsql(connection));
    services.AddScoped<IJobRepository, JobRepository>();
}
  

Затем я определяю свой JobTrackingContext следующим образом:

 public JobTrackingContext(DbContextOptions<JobTrackingContext> options)
            : base(options)
{
    public DbSet<Job> Jobs { get; set; }
}
  

Теперь я могу определить репозиторий для фактического создания / редактирования / удаления заданий:

  public class JobRepository : GenericRepository<Job, long>, IJobRepository
{
        private Job currentJob;
        public JobRepository(JobTrackingContext jobTrackingContext, JobTrackingSettings settings)
        : base(jobTrackingContext)
    {
        _settings = settings;
    }
        public async Task StartSync(JobType jobType, JobTriggerType jobTriggerType)
        {
            var tempJob = new Job(jobType, jobTriggerType);
            await _dbContext.Jobs.AddAsync(tempJob);
            await _dbContext.SaveChangesAsync();
        }
}
  

И весь этот код создается посредством Post-запроса к этому API:

 public async void Post()
{
    _logger.LogDebug("Going to start account sync");
    await _jobRepository.StartSync(JobType.ZRequestSync, JobTriggerType.Scheduled);
    try
    {
        await _sync.StartAsync();
        await _jobRepository.ChangeSyncStatus(JobStatusType.Finished);
    }
    catch (Exception e)
    {
        _logger.LogError(e, "Error occured during sync :(");
        await _jobRepository.ChangeSyncStatus(JobStatusType.Failed);
    }
}
  

Тем не менее, когда я делаю это, я получаю исключение с сообщением Reset() called on connector with state Connecting . Я не понимаю, откуда это взялось.
Когда я не использую введенную версию, но вместо этого делаю это:

 using (var c = new JobTrackingContext())
{
    var job = new Job(jobType, jobTriggerType)
    await c.Jobs.AddAsync(job);
    await c.SaveChangesAsync();
}
  

Кажется, все работает нормально. Кажется, что контекст удаляется слишком рано. Но как я могу предотвратить это и / или что я упускаю?

Полная трассировка стека:

     System.ObjectDisposedException
  HResult=0x80131622
  Message=Cannot access a disposed object. A common cause of this error is disposing a context that was resolved from dependency injection and then later trying to use the same context instance elsewhere in your application. This may occur if you are calling Dispose() on the context, or wrapping the context in a using statement. If you are using dependency injection, you should let the dependency injection container take care of disposing context instances.
  Source=Microsoft.EntityFrameworkCore
  StackTrace:
   at Microsoft.EntityFrameworkCore.DbContext.CheckDisposed()
   at Microsoft.EntityFrameworkCore.DbContext.get_DbContextDependencies()
   at Microsoft.EntityFrameworkCore.DbContext.<SaveChangesAsync>d__52.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter1.GetResult()
   at ZShared.JobRepository.<StartSync>d__4.MoveNext() in C:UsersrichaDocumentsCodesCompanyProductSharedFolderJobRepository.cs:line 38
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at ZAccountSyncService.AccountSyncController.<Post>d__4.MoveNext() in C:UsersrichaDocumentsCodeCompanyProductSubProductAccountSyncController.cs:line 32
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Threading.ThreadPoolWorkQueue.Dispatch()
  

Комментарии:

1. Откуда это _dbContext взялось в вашем JobRepository примере кода? Кажется, у вас есть весь класс в примере, но нет ни конструктора, ни поля с именем _dbContext

2. Он сохраняется в суперклассе, который я теперь сделал более понятным

3. Вы пробовали .AddDbContext вместо .AddDbContextPool ? Работает ли это? Какова ваша область действия? Что это за приложение? ASP.NET (Ядро) или что-то другое? Кроме того: ваш последний пример кода может работать, потому что в нем отсутствует метод await при вызове .AddAsync . Работает ли это, когда вы добавляете туда забытый await ?

4. Да, я пытался изменить его на .AddDbContext , и, к сожалению, это не решает проблему. Область действия чего? Мой JobRepository ограничен, как и большинство моих классов. И так JobTrackingContext . Да, это приложение ASP.NETCORE, и добавление await не нарушает мой другой пример.

5. («scoped» означает, что вы получаете один экземпляр на службу с ограниченной областью внутри области. Какой длины область зависит от вас — в ASP.NET Ядро, область создается по веб-запросу по умолчанию.) Когда вы получаете исключение? При вызове SaveChangesAsync ? Не могли бы вы предоставить полную трассировку стека?

Ответ №1:

Суть проблемы заключается в объявлении вашего AccountSyncController.Post метода. У вас есть async void Post() вместо async Task Post() .

Когда задачи нет, запросу нечего ожидать, и он завершается до завершения вызова метода _sync.StartAsync() . С завершением запроса также заканчивается область действия. По окончании срока службы все экземпляры с ограниченным сроком службы удаляются. Таким образом, ваш контекст удаляется до того, как вы перейдете к вызову SaveChanges метода. И это является причиной вашего исключения.

Комментарии:

1. Спасибо, что помогли с этой проблемой и уделили столько времени, сколько вы сделали