EF Core - Context 上的多个异步调用导致错误

问题描述 投票:0回答:2

这是我的情况。我创建了一个管理产品库存的 blazor 服务器应用程序。我有多个存储库,它们使用相同的数据库上下文来搜索或查询数据库实体。

在我的库存页面中,我有一个异步调用,它根据搜索参数搜索所有用户的库存产品(每次用户在搜索输入字段中输入字母时都会调用搜索)。当在短时间内多次调用搜索查询时,我似乎收到此错误:

“在上一个操作完成之前,在此上下文实例上启动了第二个操作”

这是我的数据库配置:

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

DI:

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

services.AddScoped<IUserInventoryProductMappingService, UserInventoryProductMappingService>();

这是来自剃刀组件的调用:

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

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

我的服务:

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

我的存储库:

 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;
                }
            }

我已确保我的询问都得到妥善处理。我还尝试将数据库上下文切换为瞬态服务生命周期,但没有成功。我的服务、存储库和上下文正在使用范围服务生命周期。在这种情况下我做错了什么?谢谢你的帮助。

c# .net entity-framework asp.net-core
2个回答
6
投票

我建议您查看 Blazor 的服务生命周期文档:

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

在 Blazor 中,作用域服务大多在每个用户会话中实例化一次。它基本上是每个用户的单例。

将 DBContext 更改为瞬态不会执行任何操作,因为存储库仍处于作用域范围内,因此每个会话仍仅注入一次 DBContext。

您将有多种选择,我认为最简单的是使用 DBContextFactory 或 PooledDBContextFactory 并为每个工作单元实例化一次新上下文。

看这里: https://learn.microsoft.com/en-us/ef/core/dbcontext-configuration/#using-a-dbcontext-factory-eg-for-blazor


0
投票

除了 Matt Bommicino 的回答之外,它解释了 Blazor 应用程序中 dbcontext 的生命周期。重要的是不是—— “Entity Framework Core 不支持在同一 DbContext 实例上运行多个并行操作。” 这包括异步查询的并行执行以及来自多个线程的任何显式并发使用。因此,始终立即等待异步调用,或使用单独的 DbContext 实例来执行并行执行的操作。

当 EF Core 检测到并发使用 DbContext 实例的尝试时,您将看到 InvalidOperationException 并显示如下消息:

在前一个操作完成之前,第二个操作已在此上下文中启动。这通常是由于不同的线程使用同一个 DbContext 实例引起的,但是实例成员不能保证线程安全。

当并发访问未被检测到时,可能会导致未定义的行为、应用程序崩溃和数据损坏。

有一些常见错误可能会无意中导致同一 DbContext 实例上的并发访问:

请参阅此处 https://learn.microsoft.com/en-us/ef/core/dbcontext-configuration/

© www.soinside.com 2019 - 2024. All rights reserved.