多线程 DbContext 操作

以下代码(在单个 DbContext 上)导致“在上一个操作完成之前在此上下文上启动了第二个操作”。

[HttpGet]
[Route("api/[controller]/circuit")]
public async Task<IEnumerable<object>> GetallCircuits()
{
    var circuits = await Task.WhenAll((await _context.Circuits.ToListAsync()).Select(async x => new
    {
        x.Id,x.FastestLap,x.LengthInmiles,Country = await _context.Countries.FirstOrDefaultAsync(c => c.Id == x.CountryId),Map = await _context.Maps.FirstOrDefaultAsync(m => m.Id == x.MapId),Locations = await _context.Locations.Where(l => l.CircuitId == x.Id).ToListAsync()
    }));

    return circuits;
}

我能够通过去掉 async/awaitTask.WhenAll 部分并用 .Result 替换它们来解决这个问题,这在 .NET 中似乎是一个很大的禁忌。下面的固定示例:

[HttpGet]
[Route("api/[controller]/circuit")]
public async Task<IEnumerable<object>> GetallCircuits()
{
    var circuits = (await _context.Circuits.ToListAsync()).Select(x => new
    {
        x.Id,Country = _context.Countries.FirstOrDefaultAsync(c => c.Id == x.CountryId).Result,Map = _context.Maps.FirstOrDefaultAsync(m => m.Id == x.MapId).Result,Locations = _context.Locations.Where(l => l.CircuitId == x.Id).ToListAsync().Result
    });

    return circuits;
}

我的三个问题是:

  1. 为什么会发生这种情况?
  2. “固定”代码是否干净?如果没有,请您提出更好的方法吗?
  3. 我可以只使用 .ToList() 而不是异步变体吗?

谢谢!

ccnc402 回答:多线程 DbContext 操作

为什么会发生这种情况?

DbContext 不允许在同一个数据库连接上进行多个操作。在这种情况下,您有一个调用 (ToListAsync),然后是多个并发调用(Select)。

“固定”代码干净吗?如果没有,请您提出更好的方法吗?

没有。您不应使用 .Result

您的选择是:

  1. (理想)更改 LINQ 查询,使其包含一个查询中的所有必要信息,例如,使用连接或包含。这是理想的解决方案,因为只有一个查询并且数据库服务器可以最有效地处理它。
  2. 一次只执行一项操作,因为您只有一个连接。这就是 .Result 起作用的原因,但更好的解决方案是使用 await,一次执行一个,而不是将 SelectTask.WhenAll 一起使用。这种方法的缺点是一次只能完成一个操作。
  3. 保留多个操作,每个操作打开一个数据库连接。这种方法的缺点是它需要多个数据库连接。

我可以只使用 .ToList() 而不是异步变体吗?

ToListAsync 不是问题所在。问题是 Select + WhenAll

本文链接:https://www.f2er.com/4860.html

大家都在问