c# - 等待后 EF Core 异步方法挂起/死锁
问题描述
更新:这个问题实际上与异步无关——我的数据库不同步并且 LINQ 查询失败。DSharpPlus 丢弃捕获的异常,因此没有任何问题的迹象。有关更多详细信息,请参阅下面的我的自我回答。
我在尝试在 .NET 5 上使用 EF Core 的异步方法时遇到了挂起/死锁。这是在控制台应用程序中(所以没有 ASP.NET、WPF 或类似的东西),但我使用的是Microsoft.Extensions .Hosting包来管理应用程序的生命周期。我还使用了第 3 方中间件DSharpPlus,它负责将工作分派到线程池中,这是我调用 EF 的地方。依赖注入正在使用中,通过 Host.CreateDefaultBuilder 配置。
DSharpPlus 从 IHostedService 异步运行(通过作用域 DI 包装器 - 因为托管服务必须是单例并且 DbContext 是作用域的)。在从新的 DI 范围创建处理程序代码的新实例后,它将事件分派到线程池(通过 Task.Run)。处理程序代码调用注入服务的新创建实例,该实例最终调用 DbContext(也是一个新实例)。
这比我想的要复杂,但是我读过的所有内容都表明它应该可以工作。不幸的是,当我尝试使用 EF 时它会中断。任何使用 EF 的代码在等待时都会挂起,但是当 EF 被存根时,相同的代码仍然有效。这是最短/最简单的事务(为了便于阅读而减少了一点):
// This line is from DSharpPlus (not my code) but I'm including it for clarity.
// This line is called at end of a network event handler and exists only to dispatch the event to a thread pool (it immediately returns after this line)
// ExecuteCommandAsync is responsible for creating a new DI scope and fresh instance of the command handler, which contains the function below.
_ = Task.Run(async () => await this.ExecuteCommandAsync(ctx).ConfigureAwait(false));
// Called eventually by above code, inside a fresh object with a new DI scope
// _hydrationLeaderboard, _hydrationOptions, and _logger are injected each time.
// _logger is a standard .NET ILogger instance.
public async Task ScoresCommand(CommandContext ctx)
{
using (_logger.BeginScope($"CmdScores.ScoresCommand@{ctx.Message.Id.ToString()}"))
{
_logger.LogDebug("Requested by [{user}]", ctx.User);
// This never returns
var leaderboard = await _hydrationLeaderboard.GetLeaderboard(_hydrationOptions.LeaderboardSize);
// ** snipped **
}
}
// Called by above code
// _selfcareDb is new DbContext instance from DI
public Task<List<HydrationLeaderboardEntry>> GetLeaderboard(int top = 3)
{
// This never returns. The original has additional LINQ, but it still fails like this too.
// It also fails if method is async/await
return _selfcareDb.UserScores
.Select((us, idx) => new HydrationLeaderboardEntry(
// **Snip**, just copying properties from DB entity to service entity
)
.ToListAsync();
}
我想我已经排除了异步挂起的常见嫌疑 - 我没有使用 .Wait、.GetResult 或任何其他类似的调用。我没有混入任何同步代码(除非我错过了)。我确保为每个任务注入一个唯一的 DbContext 实例。作为测试,我删除了所有 EF 代码并将其替换为内存字典。这工作得很好。我什至查看了 DSharpPlus 的代码,并没有发现任何可疑之处。
如果我在某处犯了明显的错误,请多多道歉!这是我第一个使用 async EF 的项目,而且我对 async/await 通常还是比较陌生。如果有帮助的话,我很乐意分享这个项目的完整代码。
提前感谢您的任何帮助!
编辑:我忘了提,我使用的数据库是 Sqlite。
更新: Task.Run 调用似乎不是问题。即使使用在单个线程中完全使用异步/等待的备用事件调度,对 EF 的调用仍然挂起。
解决方案
您应该删除.ConfigureAwait(false)
, ConfigureAwait with false 表示您没有等待任务完成,我真的建议您阅读此博客
推荐阅读
- javascript - 哪个更高效:一个全局事件侦听器或每个组件中的多个相同类型?
- apache-kafka - Avro 数据未出现在 ksql 查询中
- python - 内部的异步任务无法更改全局 INT 但可以更改 LIST
- karate - 无法从空手道的 csv 文件中读取“@”符号
- java - 如何获取在 context.xml 上配置的 Session cookie 的自定义名称
- c# - Recharger un composant Blazor après un OnChange d'un HTML sélectionné
- yocto - yocto中的通配符效果配方查找过程如何?
- apache-kafka - Kafka-Strams 清除 GlobalKTable
- python - 帖子中的 TemplateSyntaxError,第一个 Django 项目
- javascript - 我想对路由器做出反应以呈现新页面