方法context.Orders.RemoveRange引发InvalidOperationException

问题描述 投票:0回答:1

方法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解决此问题?

asp.net-core .net-core entity-framework-core
1个回答
0
投票

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();
}
© www.soinside.com 2019 - 2024. All rights reserved.