c# - 如何从 EF 的非异步 SaveChanges 安全地调用异步方法?

标签 c# entity-framework asynchronous async-await entity-framework-core

我正在使用 ASP.NET Core 和具有 SaveChangesSaveChangesAsync 的 EF Core。

在保存到数据库之前,在我的 DbContext 中,我执行了一些审计/日志记录:

public async Task LogAndAuditAsync() {
    // do async stuff
}

public override int SaveChanges {
    /*await*/ LogAndAuditAsync();      // what do I do here???
    return base.SaveChanges();
}

public override async Task<int> SaveChangesAsync {
    await LogAndAuditAsync();
    return await base.SaveChanges();
}

问题是同步 SaveChanges()

我总是“一直异步”,但在这里这是不可能的。我可以重新设计以具有 LogAndAudit()LogAndAuditAsync() 但这不是 DRY,我需要更改其他一些不属于的主要代码对我来说。

关于这个主题还有很多其他问题,而且都是笼统的、复杂的、充满争议的。 我需要知道在这种特定情况下最安全的方法

那么,在 SaveChanges() 中,我该如何安全、同步地调用异步方法,而不会出现死锁?

最佳答案

从非异步方法调用异步方法的最简单方法是使用 GetAwaiter().GetResult():

public override int SaveChanges {
    LogAndAuditAsync().GetAwaiter().GetResult();
    return base.SaveChanges();
}

这将确保 LogAndAuditAsync 中抛出的异常不会在 SaveChanges 中显示为 AggregateException。而是传播原始异常。

但是,如果代码在特殊的同步上下文中执行,在同步异步时可能会死锁(例如 ASP.NET、Winforms 和 WPF),那么您必须更加小心。

每次 LogAndAuditAsync 中的代码使用 await 时,它都会等待任务完成。如果此任务必须在当前被 LogAndAuditAsync().GetAwaiter().GetResult() 调用阻塞的同步上下文上执行,您就会遇到死锁。

为避免这种情况,您需要将 .ConfigureAwait(false) 添加到 LogAndAuditAsync 中的所有 await 调用。例如

await file.WriteLineAsync(...).ConfigureAwait(false);

请注意,在此 await 之后,代码将在同步上下文之外(在任务池调度程序上)继续执行。

如果这不可能,您最后的选择是在任务池调度程序上启动一个新任务:

Task.Run(() => LogAndAuditAsync()).GetAwaiter().GetResult();

这仍然会阻塞同步上下文,但 LogAndAuditAsync 将在任务池调度程序上执行,而不是死锁,因为它不必进入被阻塞的同步上下文。

关于c# - 如何从 EF 的非异步 SaveChanges 安全地调用异步方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41206510/

相关文章:

c# - 在用户输入时加快记录过滤

c# - Xamarin 中的蓝牙打印

c# - 在 .NET RESTful 应用程序中使用 Ninject?

entity-framework - EF 4.3(代码优先)- 确定何时将项目添加到虚拟 ICollection 属性

javascript - node.js 异步 waterfall 调用以前的方法

c# - textchanged 事件是否可以仅在 textBox 获得焦点时发生?

c# - EF - context.Set<t> 与 context.T

c# - MySql 连接字符串的 Entity Framework 错误

emacs - 在 elisp 中异步等待进程完成?

javascript - 同步 jQuery 动画