Roland Deschain
Roland Deschain

Reputation: 2850

Blazor Server: "System.InvalidOperationException: A second operation was started on this context before a previous operation completed"

I have the following OnAfterRenderAsync function in my blazor page:

protected override async Task OnAfterRenderAsync(bool firstRender)
{
    try
    {
        if (firstRender)
        {
            await JSRuntime.InvokeVoidAsync("initializeDropZone");

            var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
            var user = authState.User;


            if (user.Identity.IsAuthenticated)
            {
                var userSecurityInfo = await UserManager.GetUserAsync(user);
                var userDbImages = (await ImageData.GetImagesByUser(userSecurityInfo.Id)).ToList();
                foreach (var dbImage in userDbImages)
                {
                    _userFiles.Add(new ImageModel()
                    {
                        ID = dbImage.ID,
                        DatasetName = dbImage.DatasetName,
                        Name = dbImage.FileName,
                        UploadDate = dbImage.UploadDate,
                        BucketUrl = dbImage.BucketUrl,
                        BucketUrlExpireDate = dbImage.BuckeUrlExpireDate
                    });
                }
            }
            StateHasChanged();
        }
    }
    catch(Exception e)
    {
        Console.WriteLine("OnAfterRenderError (DataManagement):" + e.Message);
    }
    await base.OnAfterRenderAsync(firstRender);

    return;
}

While normally this operation is quite fast (it only loads image URLs instead of actual image data) after the first startup of the server it takes a bit. If the user decides to change pages while the operation is still running I get the following error:

fail: Microsoft.EntityFrameworkCore.Query[10100] An exception occurred while iterating over the results of a query for context type 'WebApplication.Data.ApplicationDbContext'. System.InvalidOperationException: A second operation was started on this context before a previous operation completed. This is usually caused by different threads concurrently using the same instance of DbContext. For more information on how to avoid threading issues with DbContext, see https://go.microsoft.com/fwlink/?linkid=2097913. at Microsoft.EntityFrameworkCore.Internal.ConcurrencyDetector.EnterCriticalSection() at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable1.AsyncEnumerator.MoveNextAsync() System.InvalidOperationException: A second operation was started on this context before a previous operation completed. This is usually caused by different threads concurrently using the same instance of DbContext. For more information on how to avoid threading issues with DbContext, see https://go.microsoft.com/fwlink/?linkid=2097913. at Microsoft.EntityFrameworkCore.Internal.ConcurrencyDetector.EnterCriticalSection() at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable1.AsyncEnumerator.MoveNextAsync()

Any suggestions on how to fix this and understand the issue better are appreciated. So far I simply catch the exception, so the application doesn't crash, but I would like to prevent it in the first place.

Upvotes: 0

Views: 940

Answers (1)

MrC aka Shaun Curtis
MrC aka Shaun Curtis

Reputation: 30167

My guess is that this line:

var userDbImages = (await ImageData.GetImagesByUser(userSecurityInfo.Id)).ToList();

is still executing on the DbContext when you change page, so whatever DB call gets made on the new page it fails because the only DbContext is already in use. I'm guesssing because at this point I don't know what ImageData is!

The DbContext Factory solves these problems, so you need to sort out what issues you have with implementing the Factory because you can't run async database operations against a single context in a real application. Post a question on your Factory problem and we will see if we can help.

A further comment on design: use a scoped view service to hold the image data so you only get it once: ignore if I'm reading this code wrongly and you are already doing so in ImageData.

Upvotes: 1

Related Questions