方法context.Orders.RemoveRange引发了InvalidOperationException。它从多个任务中调用。我试图锁定context.Orders.RemoveRange,但引发了相同的异常。
例外是:
InvalidOperationException: A second operation started on this context before a previous operation completed. Any instance members are not guaranteed to be thread safe.
这是引发异常的源代码
public class Foo : IFoo
{
private MyContext context;
public Foo(MyContext context)
{
this.context = context;
}
public async Task Update(Order order)
{
context.Orders.RemoveRange(context.Orders.Where(r => r.CustomerID == 100));
context.Orders.RemoveRange(context.Orders.Where(r => r.CustomerID == 120));
order.EmployeeID = 2;
context.Update(order);
await context.SaveChangesAsync();
}
}
异常堆栈跟踪:
at Microsoft.EntityFrameworkCore.Internal.ConcurrencyDetector.EnterCriticalSection()
at Microsoft.EntityFrameworkCore.Query.Internal.LinqOperatorProvider.ExceptionInterceptor`1.EnumeratorExceptionInterceptor.MoveNext()
at Microsoft.EntityFrameworkCore.DbContext.RemoveRange(IEnumerable`1 entities)
at WebApplication2.Foo.Update(Order order) in D:\Projects\RemoveRangeIssue\WebApplication2\Foo.cs:line 24
我将小项目添加到GitHub,以重现上述问题。这是一个link。它具有Task.WaitAll以在两个线程中运行两个方法。如何在不删除Task.WaitAll的情况下从多个任务调用方法context.Orders.RemoveRange解决此问题?
context.Orders.Where(r => r.CustomerID == 100)
这只会返回一个代表查询但尚未执行的IQueryable。然后,当您使用RemoveRange
隐式迭代该可查询对象时,将执行查询。
这对于EntityFramework通常不是一个好主意。您应该始终明确地使用ToListAsync()
或ToArrayAsync()
执行查询:
public async Task Update(Order order)
{
var ordersToRemove = await context.Orders
.Where(r => r.CustomerID == 100 || r.CustomerID == 120)
.ToListAsync();
context.Orders.RemoveRange(ordersToRemove);
order.EmployeeID = 2;
context.Update(order);
await context.SaveChangesAsync();
}