Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
281 views
in Technique[技术] by (71.8m points)

c# - 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. DSharpPlus discards caught exceptions so there was no indication of any problem. 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 also using a 3rd party middleware DSharpPlus which is responsible for dispatching work onto a thread pool, which is where I call into EF. Dependency injection is in use, configured via Host.CreateDefaultBuilder.

DSharpPlus is run asynchronously from an IHostedService (via a scoped DI wrapper - since hosted services must be singletons and DbContext is scoped). It dispatches events onto a thread pool (via Task.Run) after creating a new instance of the handler code from a fresh DI scope. The handler code calls into a newly created instance of an injected service, which finally calls into the DbContext (also a fresh instance).

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. Any code using EF hangs as soon as it awaits, but the same code works when EF is stubbed out. 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. 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. As a test, I removed all of the EF code and replaced it with an in-memory Dictionary. This worked fine. I even reviewed the code of DSharpPlus and didn't see anything suspicious.

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. 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.

UPDATE: The Task.Run call does not seem to be the issue. The calls to EF still hang even when using an alternate event dispatch that uses entirely async/await within a single thread.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...