-3

I am using Blazor. Why does the SaveChangesAsync.Wait() method in the code below return no response?

It reflects in the database, the update is done, but it waits forever. Why?

I don't want to use the synchronous SaveChanges() method. I want to know the cause of this problem.

private void Run()
{
    try
    {
        var id = Guid.Parse("5C7B0B3D-08FA-406D-ABCA-18EE33321B55");

        var data = DbContext.Set<MyModel>().AsTracking(QueryTrackingBehavior.TrackAll)
                  .Single(x => x.Id == id);

        data.UpdateDate = DateTimeOffset.Now;
    }
    finally
    {
        DbContext.SaveChangesAsync(CancellationToken.None)
        .Wait(CancellationToken.None); // <-- waiting here forever. Why?
    }
}

where called:

private async Task HandleValidSubmit()
{
    Run();

    await JS.InvokeVoidAsync("swalInfo", null, "Demo");
}

razor.cs:

<EditForm Model="@dto" OnValidSubmit="HandleValidSubmit">...
kursat sonmez
  • 818
  • 1
  • 12
  • 22
  • 4
    Don't call `SaveChangesAsync` unless you're in an `async` method, and **never** use `.Wait()` or `.Result` on an incomplete `Task`: https://stackoverflow.com/questions/13140523/await-vs-task-wait-deadlock - change your code to use `dbContext.SaveChanges();` - not the async version. – Dai Apr 20 '23 at 18:13
  • Why are you not calling SaveChanges when you are not awaiting the Task returned by Async call – Tomas Chabada Apr 20 '23 at 18:13
  • 4
    _"but it waits forever. Why?"_ - because your code is deadlocking because you're using `.Wait()` when you're not meant to. – Dai Apr 20 '23 at 18:14
  • 1
    The problem is that Task continuation can not contact the calling context thread to continue because context thread is already blocked by synchronously waiting for Task completion – Tomas Chabada Apr 20 '23 at 18:15
  • You may want to read about the [async-await pattern](https://learn.microsoft.com/dotnet/csharp/asynchronous-programming/task-asynchronous-programming-model) in .NET. – Julian Apr 20 '23 at 18:17
  • Thanks for your answers. But it works while in BackgroundService. Shouldn't it have the same reaction here? – kursat sonmez Apr 20 '23 at 18:19
  • 1
    Related: [An async/await example that causes a deadlock](https://stackoverflow.com/questions/15021304/an-async-await-example-that-causes-a-deadlock). – Theodor Zoulias Apr 20 '23 at 18:26
  • 1
    It works in a background service because that's probably not running on the UI thread. It still blocks that thread. [Polite] If you don't understand that you're blocking threads, then you're out of your depth. Take the advice provided. – MrC aka Shaun Curtis Apr 20 '23 at 21:12

1 Answers1

2

The basic rule is "Async All The Way" and "Keep it Simple". It's async in method declarations and await in calls. Unless you're an Async specialist, anything else is guesswork and voodoo.

Your Run should look something like this.

See this on how to use a DBContext factory - https://learn.microsoft.com/en-us/aspnet/core/blazor/blazor-server-ef-core?view=aspnetcore-7.0

private async ValueTask RunAsync()
{
        // You should be using a DbContextFctory here

        using var dbContext = _factory.CreateDbContext();

        var id = Guid.Parse("5C7B0B3D-08FA-406D-ABCA-18EE33321B55");

        var data = await dbContext.Set<MyModel>().AsTracking(QueryTrackingBehavior.TrackAll)
                  .SingleOrDefaultAsync(x => x.Id == id);

        if(data is not null)
        {
            data.UpdateDate = DateTimeOffset.Now;
            var result = await dbContext.SaveChangesAsync(CancellationToken.None);
            if(result == 1)
            {
                // onbly one update so all ok
            }
        }
    }
}

Then :

private async Task HandleValidSubmit()
{
    await RunAsync();

    await JS.InvokeVoidAsync("swalInfo", null, "Demo");
}
MrC aka Shaun Curtis
  • 19,075
  • 3
  • 13
  • 31