Использование EventHandler с Entity Framework: вторая операция началась в этом контексте до завершения предыдущей асинхронной операции - PullRequest
0 голосов
/ 22 апреля 2019

Я пытаюсь обновить свою базу данных на основе некоторых событий.Всякий раз, когда A генерирует событие, B выполнит обновление БД.Я продолжаю получать следующее исключение :

"Вторая операция началась в этом контексте перед завершением предыдущей асинхронной операции. Используйте await, чтобы убедиться, что любые асинхронные операции завершены перед вызовом другого методав этом контексте. Любые члены экземпляра не гарантируют поточно-ориентированность. "

Я понимаю исключение, но не знаю, откуда оно и связано ли оно с использованием EventHandler.

После долгих поисков:

  • Я убедился, что все ждет, чтобы убедиться, что нет параллельных вызовов с dbcontext
  • Я удостоверился, что подписано только ОДНО событие (это былосоветы по другим вопросам, связанным со стековым потоком)
  • Я подписался на событие с async/await
  • Я пытался Task.WaitAll() во всех возможных комбинациях
class A {
    public event EventHandler<LogArgs> LogEvent;


    public void WorkInA() {
        .....
        this.SendLogEvent();
        .....
        this.SendLogEvent();
        .....
    }

    private void SendLogEvent() {
        this.LogEvent(this, args);
    }

}
class B {
    private A a;

    public async void Init() {
        //Before: a.LogEvent += LogEventToDB;
        a.LogEvent += async (s, e) => await LogEventToDB(s, e);

    }

    public void DoWork() {
        .....
        a.WorkInA();
        .....
    }


    private async Task LogEventToDB(object sender, LogMessageEventArgs e) {
        .....
        await this.unitOfWork.SaveAsync();
        .....
    }
}
//Inside unitOfWork
public async Task SaveAsync()
{
       await this.context.SaveChangesAsync();
}

Я хотел бы знать, что вызывает эту проблему и возможно ли ее исправить.

1 Ответ

0 голосов
/ 22 апреля 2019

Да, ваша проблема связана с обработкой событий. Более конкретно, это связано с async void. Обе из строк кода ниже попадают в ловушку async void:

//Before: a.LogEvent += LogEventToDB;
a.LogEvent += async (s, e) => await LogEventToDB(s, e);

Одна из проблем с async void заключается в том, что вызывающий код не может знать, когда он завершился.

Если вы действительно хотите использовать события для сообщений журнала (что мне немного странно), то вы должны иметь возможность просто добавить сообщение журнала в очередь (синхронно) и иметь отдельную «рабочую» почту для перетаскивания. вне очереди и отправка их в базу данных (асинхронно).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...