我在一个运行带有Sqlite的实体框架核心的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:Error:迭代查询结果时,数据库中出现异常。System.ObjectDisposedException:安全句柄已关闭
- Microsoft.EntityFrameworkCore.Query.RelationalQueryCompilationContextFactory:Error:迭代查询结果时,数据库中出现异常。System.InvalidOperationException:只有在连接打开时才能调用ExecuteReader。
在我看来,这两个错误都表明DbContext
发生了一些事情,比如过早处置(尽管不是DbContext
本身)。DbContext
使用DotNet Core的管道“通常”注入到控制器构造函数中,配置如下所示(来自我的Startup.cs
ConfigureServices
方法体):
services.AddDbContext<ApplicationContext>(options => options.UseSqlite(connectionString));
如果我将生成上述代码的错误更改为如下所示:
var r1 = await _dbContext.Awesome.FirstOrDefaultAsync(a => [...]);
var r2 = await _dbContext.Bazinga.FirstOrDefaultAsync(b => [...]);
..。我没有看到提到的错误,因此得出的结论是,在我的DbContext
(如上所述注入)的同一示例上并发运行多个任务是导致问题的原因。显然,这是一个意想不到的问题。
问题:
1.我得出的结论是正确的吗,还是有别的事情在发生?
1.你能指出为什么这些错误只是偶尔发生吗?
1.您是否知道在DbContext
上仍在运行并发任务的同时避免该问题的简单方法?
2条答案
按热度按时间gz5pxeao1#
不幸的是,你不能这么做。
从EF Core documentation
EF Core不支持在同一上下文示例上运行多个并行操作。在开始下一个操作之前,您应该始终等待操作完成。这通常是通过在每个异步操作上使用AWAIT关键字来完成的。
同样来自EF 6文档
线程安全
虽然线程安全会使异步更有用,但它是一个正交特性。目前还不清楚在最一般的情况下我们是否能实现对它的支持,因为EF与由用户代码组成的图交互来维护状态,而且没有简单的方法来确保该代码也是线程安全的。
目前,EF将检测开发人员是否试图一次执行两个异步操作并抛出。
DbContext
在任何时间点仅支持单个开放数据读取器。如果希望同时执行多个数据库查询,则需要多个DbContext
示例,每个示例对应一个并发查询。至于为什么错误偶尔会发生,这是一个竞争条件。仅仅因为您一个接一个地启动两个任务(不等待),并不能保证数据库会同时命中。有时执行时间恰好一致,而有时一个任务可能在另一个任务开始时正确完成,因此不存在冲突。
如何避免它-不要这样做,因为它不受支持。等待您的每个DbContext调用或使用多个DbContext示例。
kxkpmulp2#
根据文档,EF当然不支持在同一上下文示例上运行多个并行操作,但是您可以使用DbConextOptions为相同的数据库上下文创建其他上下文
然后您可以在您的控制器中使用
现在在您的方法中,您可以调用任务,每个任务都将使用自己的上下文,例如
并且您必须编写以下函数