简体   繁体   English

等待后 EF Core 异步方法挂起/死锁

[英]EF Core async methods hang/deadlock after await

Update: The issue was actually unrelated to async - my database was out of sync and the LINQ queries were failing.更新:这个问题实际上与异步无关——我的数据库不同步并且 LINQ 查询失败。 DSharpPlus discards caught exceptions so there was no indication of any problem. DSharpPlus 丢弃捕获的异常,因此没有任何问题的迹象。 See my self-answer below for more details.有关更多详细信息,请参阅下面的我的自我回答。

I am encountering a hang/deadlock while attempting to use the async methods of EF Core on .NET 5. This is in a console app (so no ASP.NET, WPF, or anything like that) but I am using the Microsoft.Extensions.Hosting package to manage the application lifecycle. I am encountering a hang/deadlock while attempting to use the async methods of EF Core on .NET 5. This is in a console app (so no ASP.NET, WPF, or anything like that) but I am using the Microsoft.Extensions.Hosting package管理应用程序生命周期。 I am also using a 3rd party middleware DSharpPlus which is responsible for dispatching work onto a thread pool, which is where I call into EF.我还使用了第 3 方中间件DSharpPlus ,它负责将工作分派到线程池中,这是我调用 EF 的地方。 Dependency injection is in use, configured via Host.CreateDefaultBuilder.依赖注入正在使用中,通过 Host.CreateDefaultBuilder 配置。

DSharpPlus is run asynchronously from an IHostedService (via a scoped DI wrapper - since hosted services must be singletons and DbContext is scoped). DSharpPlus 从 IHostedService 异步运行(通过作用域 DI 包装器 - 因为托管服务必须是单例并且 DbContext 是作用域的)。 It dispatches events onto a thread pool (via Task.Run) after creating a new instance of the handler code from a fresh DI scope.在从新的 DI scope 创建处理程序代码的新实例后,它将事件分派到线程池(通过 Task.Run)。 The handler code calls into a newly created instance of an injected service, which finally calls into the DbContext (also a fresh instance).处理程序代码调用注入服务的新创建实例,该实例最终调用 DbContext(也是一个新实例)。

This is more complex than I would like, but everything I've read suggests that it should work.这比我想的要复杂,但是我读过的所有内容都表明它应该可以工作。 Unfortunately, it breaks when I attempt to use EF.不幸的是,当我尝试使用 EF 时它会中断。 Any code using EF hangs as soon as it awaits, but the same code works when EF is stubbed out.任何使用 EF 的代码在等待时都会挂起,但是当 EF 被存根时,相同的代码仍然有效。 This is the shortest/simplest transaction (reduced a bit for readability):这是最短/最简单的事务(为了便于阅读而减少了一点):

// 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();
}

I think I've ruled out the usual suspects for async hangs - I am not using.Wait, .GetResult, or any other calls like that.我想我已经排除了异步挂起的常见嫌疑人——我没有使用.Wait、.GetResult 或任何其他类似的调用。 I don't have any synchronous code mixed in (unless I missed it).我没有混入任何同步代码(除非我错过了)。 I have made sure that a unique DbContext instance is injected for each task.我确保为每个任务注入一个唯一的 DbContext 实例。 As a test, I removed all of the EF code and replaced it with an in-memory Dictionary.作为测试,我删除了所有 EF 代码并将其替换为内存字典。 This worked fine.这工作得很好。 I even reviewed the code of DSharpPlus and didn't see anything suspicious.我什至查看了 DSharpPlus 的代码,并没有发现任何可疑之处。

Many apologies if I've made an obvious mistake somewhere!如果我在某处犯了明显的错误,请多多道歉! This is my first project using async EF and I'm still relatively new to async/await generally.这是我第一个使用 async EF 的项目,而且我对 async/await 通常还是比较陌生。 I would be happy to share the full code of this project if that would be helpful.如果有帮助的话,我很乐意分享这个项目的完整代码。

Thanks in advance for any assistance!提前感谢您的任何帮助!

EDIT: I forgot to mention, the database that I'm using is Sqlite.编辑:我忘了提,我使用的数据库是 Sqlite。

UPDATE: The Task.Run call does not seem to be the issue.更新: Task.Run 调用似乎不是问题。 The calls to EF still hang even when using an alternate event dispatch that uses entirely async/await within a single thread.即使使用在单个线程中完全使用异步/等待的备用事件调度,对 EF 的调用仍然挂起。

You should remove the .ConfigureAwait(false) , ConfigureAwait with false means you are not waitind the task to complete, I really suggest you to read this blog您应该删除.ConfigureAwait(false) ,ConfigureAwait 带有 false 表示您没有等待任务完成,我真的建议您阅读此博客

It was a DB error, I messed up my EF migrations so the database was empty.这是一个数据库错误,我搞砸了我的 EF 迁移,所以数据库是空的。 which obviously throws an exception.这显然会引发异常。 Somewhere along the way the exceptions were swallowed up and hidden.在此过程中的某个地方,异常被吞并并隐藏起来。 I fixed the DB errors and now everything works.我修复了数据库错误,现在一切正常。

I also added a simple try/catch block to each command handlers and now I'm getting output whenever there are errors.我还在每个命令处理程序中添加了一个简单的 try/catch 块,现在只要出现错误,我就会得到 output。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM