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

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

我收到此错误:“System.InvalidOperationException:'在上一个操作完成之前在此上下文实例上启动了第二个操作。这通常是由不同线程同时使用同一 DbContext 实例引起的”。

这是我的服务代码:

public async Task<IQueryable<GetAllUsersDTO>> UsersById(Guid userId, CancellationToken cancellationToken)
{
try
{
    var Log = await _db.Users
                        .Include(x => x.University)
                        .Include(x => x.Fields)
                        .Where(x => x.UserId == userId)
                        .OrderByDescending(x => x.CreatedAt)
                        .AsNoTracking()
                        .ToListAsync();

    var logsDTO = await Task.WhenAll(Log.Select(async x =>
    {
        var userInDb = await _db.Users.FindAsync((Guid)x.UserId); 
        var user = await _userService.GetDirectoryInformation(userInDb, cancellationToken);
        return new GetAllUsersDTO()
        {
            Description = x.Description,
            PostDate = x.PostDate,
            Id = x.Id,
            RecieveUserId = x.RecieveUserId,
            UniversityId = x.UniversityId,
            IsVisible = x.IsVisible,
            IsApproved = x.IsApproved,
            UserIdOpen = x.UserId,
            UserName = user.UserName,
            LogTitle = x.LogTitle,
            LogNr = x.LogNr,
            Email = user.Email,
        };
    }));

    return logsDTO.AsQueryable();
}
catch (Exception ex)
{
    Console.WriteLine("Error=", ex.Message);
    throw;
}
}

这就是我在此服务中注入 DataContext 的方式:

private readonly DataContext _db;
public UserService(DataContext context) : base(context)
{
 _db = context;
}

注意:此服务仅在日志列表仅返回一项数据时有效。但是当它返回多个数据时,就会触发错误。

我尝试使用 ServiceLifetime.Scoped 配置依赖注入设置,它看起来像这样:

builder.Services
    .AddDbContext<DataContext>(opt =>
        opt.UseSqlServer(builder.Configuration.GetConnectionString("connString"))
        ,ServiceLifetime.Scoped
    );
c# .net .net-core concurrency entity-framework-core
1个回答
0
投票

注意:此服务仅在日志列表仅返回一项数据时有效。但是当它返回多个数据时,就会触发错误。

因为以下几点:

var logsDTO = await Task.WhenAll(Log.Select(async x =>
{
    var userInDb = await _db.Users.FindAsync((Guid)x.UserId); 
    var user = await _userService.GetDirectoryInformation(userInDb, cancellationToken);
    // ...
}

将安排并行执行与日志一样多的任务。并且 EF Core 数据库上下文不应该同时从不同线程使用。就是这样。

解决方法:

  1. 移动获取初始查询中的所有数据 (
    var Log = await _db.Users ...
    ) 并删除
    _userService.GetDirectoryInformation
    的使用并删除
    Task.WhenAll(Log.Select
    (我认为这是最好的做法)
  2. 在每个任务中创建内部范围并解决上下文/
    _userService
© www.soinside.com 2019 - 2024. All rights reserved.