这是我的情况。我创建了一个管理产品库存的 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;
}
}
我已确保我的询问都得到妥善处理。我还尝试将数据库上下文切换为瞬态服务生命周期,但没有成功。我的服务、存储库和上下文正在使用范围服务生命周期。在这种情况下我做错了什么?谢谢你的帮助。
我建议您查看 Blazor 的服务生命周期文档:
在 Blazor 中,作用域服务大多在每个用户会话中实例化一次。它基本上是每个用户的单例。
将 DBContext 更改为瞬态不会执行任何操作,因为存储库仍处于作用域范围内,因此每个会话仍仅注入一次 DBContext。
您将有多种选择,我认为最简单的是使用 DBContextFactory 或 PooledDBContextFactory 并为每个工作单元实例化一次新上下文。
除了 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/