将EventHandler与实体框架一起使用:在上一个异步操作完成之前,在此上下文中启动了第二个操作

问题描述 投票:0回答:1

我试图根据一些事件更新我的数据库。每当A生成事件时,B将执行数据库更新。我一直得到以下异常:

“在上一次异步操作完成之前,第二个操作在此上下文中启动。使用await确保在此上下文上调用另一个方法之前已完成任何异步操作。不保证所有实例成员都是线程安全的。”

我理解异常,但我不知道它来自何处,以及它是否与使用EventHandler有关。

经过长时间搜索:

  • 我确保一切都等待确保没有与dbcontext的并行调用
  • 我确保我只订阅了一个事件(这是来自其他stackoverflow问题的建议)
  • 我用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();
}

我想知道导致此问题的原因以及是否可以修复它。

c# entity-framework events event-handling
1个回答
0
投票

是的,您的问题是由于事件处理造成的。更具体地说,这是由于async void。以下两行代码都属于async void陷阱:

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

其中一个problems with async void是调用代码无法知道它何时完成。

如果你真的想将事件用于日志消息(这对我来说有点奇怪),那么你必须让事件只是将日志消息添加到队列中(同步),并让一个单独的“工作者”将消息从队列并将它们发送到数据库(异步)。

© www.soinside.com 2019 - 2024. All rights reserved.