避免在同一个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
的同一个实例上同时运行多个任务(如上所述注入)是导致问题的原因。 显然,这是一个意外的。
问题:
- 我得出了正确的结论,还是还有其他事情发生?
- 你能找出偶然发生错误的原因吗?
- 您是否知道在
DbContext
上仍然运行并发任务时避免此问题的任何简单方法?
不幸的是你做不到。
来自EF Core文档
EF Core不支持在同一上下文实例上运行多个并行操作。 在开始下一个操作之前,应始终等待操作完成。 这通常通过在每个异步操作上使用await关键字来完成。
也来自EF 6文档
线程安全
虽然线程安全会使异步更有用,但它是一个正交特征。 目前还不清楚我们是否可以在最常见的情况下实现对它的支持,因为EF与用户代码组成的图形交互以维持状态,并且没有简单的方法来确保此代码也是线程安全的。
目前,EF将检测开发人员是否尝试同时执行两个异步操作并抛出。
DbContext
在任何时间点仅支持单个打开的数据读取器。 如果要同时执行多个数据库查询,则需要多个DbContext
实例,每个并发查询一个。
至于为什么偶尔会发生错误,这是一种竞争条件。 只是因为你在下一个之后开始执行2个任务( 没有等待 )并不能保证数据库会同时被命中。 有时执行时间恰好排队,而其他任务可能会在另一个任务启动时完成,因此没有冲突。
如何避免它 – 不要这样做,因为它不受支持。 等待每个DbContext调用或使用多个DbContext实例。
通过查询我的意思是任何数据库操作,包括SELECT,UPDATE,DELETE,INSERT,ALTER,STORED PROCEDURE CALL,ETC