避免在同一个DbContext上同时运行多个任务的entity framework错误

我在一个运行带有Sqlite的Entity Framework Core的Dotnet Core项目中有一个WebApi控制器。

动作中的此代码会出现错误:

var t1 = _dbContext.Awesome.FirstOrDefaultAsync(a => [...]); var t2 = _dbContext.Bazinga.FirstOrDefaultAsync(b => [...]); var r1 = await t1; var r2 = await t2; 

错误是:

  • Microsoft.EntityFrameworkCore.Query.RelationalQueryCompilationContextFactory:错误:迭代查询结果时数据库中发生exception。 System.ObjectDisposedException:已关闭安全句柄

  • Microsoft.EntityFrameworkCore.Query.RelationalQueryCompilationContextFactory:错误:迭代查询结果时数据库中发生exception。 System.InvalidOperationException:只能在连接打开时调用ExecuteReader。

对我来说这两个错误都表明DbContext会发生一些事情,比如过早处置(虽然不是DbContext本身)。 DbContext是在控制器构造函数中使用Dotnet Core的管道“通常的方式”注入的,配置如下所示(来自我的Startup.cs ConfigureServices方法体):

 services.AddDbContext(options => options.UseSqlite(connectionString)); 

如果我将上面的错误产生代码改为:

 var r1 = await _dbContext.Awesome.FirstOrDefaultAsync(a => [...]); var r2 = await _dbContext.Bazinga.FirstOrDefaultAsync(b => [...]); 

…我没有看到提到的错误,因此得出的结论是在我的DbContext的同一个实例上同时运行多个任务(如上所述注入)是导致问题的原因。 显然,这是一个意外的。

问题:

  1. 我得出了正确的结论,还是还有其他事情发生?
  2. 你能找出偶然发生错误的原因吗?
  3. 您是否知道在DbContext上仍然运行并发任务时避免此问题的任何简单方法?

不幸的是你做不到。

来自EF Core文档

EF Core不支持在同一上下文实例上运行多个并行操作。 在开始下一个操作之前,应始终等待操作完成。 这通常通过在每个异步操作上使用await关键字来完成。

也来自EF 6文档

线程安全

虽然线程安全会使异步更有用,但它是一个正交特征。 目前还不清楚我们是否可以在最常见的情况下实现对它的支持,因为EF与用户代码组成的图形交互以维持状态,并且没有简单的方法来确保此代码也是线程安全的。

目前,EF将检测开发人员是否尝试同时执行两个异步操作并抛出。


DbContext在任何时间点仅支持单个打开的数据读取器。 如果要同时执行多个数据库查询,则需要多个DbContext实例,每个并发查询一个。

至于为什么偶尔会发生错误,这是一种竞争条件。 只是因为你在下一个之后开始执行2个任务( 没有等待 )并不能保证数据库会同时被命中。 有时执行时间恰好排队,而其他任务可能会在另一个任务启动时完成,因此没有冲突。

如何避免它 – 不要这样做,因为它不受支持。 等待每个DbContext调用或使用多个DbContext实例。

通过查询我的意思是任何数据库操作,包括SELECT,UPDATE,DELETE,INSERT,ALTER,STORED PROCEDURE CALL,ETC