首页 新闻 会员 周边 捐助

EF Core 报错:"A second operation started on this context before a previous operation completed"

1
悬赏园豆:50 [已解决问题] 解决于 2020-02-11 17:07

在我们的一个 ASP.NET Core 项目日志中记录了下面的错误:

System.InvalidOperationException: A second operation started on this context before a previous operation completed. This is usually caused by different threads using the same instance of DbContext. 
For more information on how to avoid threading issues with DbContext, see https://go.microsoft.com/fwlink/?linkid=2097913.
   at Microsoft.EntityFrameworkCore.Internal.ConcurrencyDetector.EnterCriticalSection()
   at Microsoft.EntityFrameworkCore.Query.RelationalShapedQueryCompilingExpressionVisitor.AsyncQueryingEnumerable`1.AsyncEnumerator.MoveNextAsync()
   at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ToListAsync[TSource](IQueryable`1 source, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ToListAsync[TSource](IQueryable`1 source, CancellationToken cancellationToken)

错误来自下面代码中的最后一行:

var tasks = new List<Task>();
var taskTodayHotUsers = _ingService.GetTopUser(DateTime.Now.Date, 12);
tasks.Add(taskTodayHotUsers);
var taskTopLuckyUsers = _ingService.GetTopLucky(DateTime.Now.Date, 12);
tasks.Add(taskTopLuckyUsers);
var taskTodayTopIngs = _ingService.GetTopIng(DateTime.Now.Date, 8);
tasks.Add(taskTodayTopIngs);
var taskRecentLuckyIngs = _ingService.GetLuckyIngList(1, 8);
tasks.Add(taskRecentLuckyIngs);
await Task.WhenAll(tasks);

model.TodayHotUsers = await taskTodayHotUsers;

请问如何解决?

dudu的主页 dudu | 高人七级 | 园豆:30775
提问于:2020-02-11 13:29
< >
分享
最佳答案
0

DbContext 不支持并发请求,add DbContext的时候修改它的scope为transient, 或者每个数据库操作都await

收获园豆:30
czd890 | 专家六级 |园豆:14488 | 2020-02-11 14:26

在 GitHub 上找到对 DbConext 不支持并发使用的原因解释,根本原因是 DbContext 所关联的数据库连接不支持并发使用。

Concurrent use of the same DbContext is not possible - and not just for tracked queries. Specifically, a DbContext has an underlying DbConnection to the database, which cannot be used concurrently. There are other various components at work under the hood which don't support multithreading.

dudu | 园豆:30775 (高人七级) | 2020-02-11 17:12
其他回答(2)
1

建议数据库访问不要用Task.whenall,每个数据库访问 await。

收获园豆:10
爱编程的大叔 | 园豆:30844 (高人七级) | 2020-02-11 14:35
0

EF 中的 DbContext 并不是线程安全的,官方文档 Asynchronous Queries 文档开头便是警告:

EF Core doesn't support multiple parallel operations being run on the same context instance. You should always wait for an operation to complete before beginning the next operation. This is typically done by using the await keyword on each async operation.

这么设计应该是处于安全方面的考虑,在异步多线程的情况下,当一个线程创建 DbContext 对象,然后进行一些实体状态修改,还没有来得及将状态保存,另一个线程也进行了同样的操作,第一个线程可以 SaveChanges 成功,但是第二个线程肯定会报错,因为实体状态已经被另外一个线程中的 DbContext 应用了。

建议去掉Task.WhenAll,查询加上 await。还有一种方案可能可行,在数据库链接字符串中添加 MultipleActiveResultSets=True 。 还是推荐使用await更稳一些。

收获园豆:10
ohyex | 园豆:1696 (小虾三级) | 2020-02-11 16:04
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册