Home > database >  EF Core - multiple async calls on Context causes error
EF Core - multiple async calls on Context causes error

Time:08-25

Here the situation I have. I created a blazor server app that manages an inventory of products. I have multiple repositories that use the same DB context in order to search or query the database entities.

In my inventory page, I have an async call that searches all the user's inventory products depending on search parameters (The search is called every time the user enters a letter in a search input field). I seem to be getting this error when the search query is called multiple times in a short amount of time:

"a second operation was started on this context instance before a previous operation completed"

Here's my Db Configuration:

 builder.Services.AddDbContext<NimaDbContext>(options =>
{
    options.UseSqlServer(builder.Configuration.GetConnectionString("NimaDbConnection"));
});

DI:

services.AddScoped<ISearcheableInventoryProductRepository<UserInventoryProductMapping>, UserInventoryProductMappingRepository>();

services.AddScoped<IUserInventoryProductMappingService, UserInventoryProductMappingService>();

Here's the call from the razor component:

private async Task SearchUserInventoryProducts(int pageNumber)
    {
        PaginationFilter.PageNumber = pageNumber;

        UserInventoryProductMappings = await _userInventoryProductMappingService
                                      .SearchByUserInventoryId(PaginationFilter, UserInventory.UserInventoryId);
    }

my service:

public async Task<PagedResponse<UserInventoryProductMappingDto>> SearchByUserInventoryId(PaginationFilter paginationFilter, int userInventoryId)
        {
            var result = await _repository.SearchByUserInventoryId(_mapper.Map<PaginationQuery>(paginationFilter), userInventoryId);

            return _mapper.Map<PagedResponse<UserInventoryProductMappingDto>>(result);
        }

my repository:

 public async Task<PagedResponse<UserInventoryProductMapping>> 

    SearchByUserInventoryId(PaginationQuery query, int userInventoryId)
            {
                try
                {
                    var defaultQuery = GetDefaultQuery().Where(x => x.UserInventoryId == userInventoryId);
    
                    if (query.SearchString != null)
                    {
                        defaultQuery = defaultQuery.Where(x => x.Product.NameLabel.LabelDescriptions.Any(x => x.Description.Contains(query.SearchString)));
                    }
    
                    if (query.SortBy != null && query.SortById != null)
                    {
                        switch (query.SortBy)
                        {
                            case "productCategory":
                                defaultQuery = defaultQuery.Where(x => x.Product.ProductCategoryId == query.SortById);
                                break;
                            case "productSubCategory":
                                defaultQuery = defaultQuery.Where(x => x.Product.ProductSubCategoryId == query.SortById);
                                break;
                        }
                    }
    
                    int count = defaultQuery.Count();
                    return new PagedResponse<UserInventoryProductMapping>
                    {
                        Data = await defaultQuery
                                .Skip((query.PageNumber - 1) * query.PageSize)
                                .Take(query.PageSize)
                                .ToListAsync(),
                        PageNumber = query.PageNumber,
                        PageSize = query.PageSize,
                        TotalPages = (int)Math.Ceiling(count / (double)query.PageSize)
    
                    };
                }
                catch (Exception e)
                {
                    _logger.LogError(e, e.Message);
    
                    throw;
                }
            }

I have made sure my queries are all awaited properly. I have also tried switching the DB context to a transient service lifetime, but without success.My services, repositories and context are using a scoped service lifetime. What am I doing wrong in this case? Thanks for helping.

CodePudding user response:

I recommend that you review the service lifetime document for Blazor:

https://docs.microsoft.com/en-us/aspnet/core/blazor/fundamentals/dependency-injection?view=aspnetcore-6.0#service-lifetime

In Blazor, scoped services are mostly instantiated one time per user session. It is basically a singleton per user.

Changing the DBContext to transient won't do anything because the repository is still scoped and therefore the DBContext is still injected only once per session.

You will have several options, I think the easiest is to use a DBContextFactory or PooledDBContextFactory and instantiate a new context once per unit of work.

See here: https://docs.microsoft.com/en-us/ef/core/dbcontext-configuration/#using-a-dbcontext-factory-eg-for-blazor

  • Related