“第二个操作就此开始" EF核心3.1并发突破性变化 [英] “A second operation started on this context” EF core 3.1 concurrency breaking change

查看:270
本文介绍了“第二个操作就此开始" EF核心3.1并发突破性变化的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在从netcoreapp 2.1迁移到3.1,我发现了我无法解决的EF core 3.1的重大更改. 以下内容在2.1中起作用,因此简单地说db上下文在设计上不是线程安全的,并且指出其他无法解决此细微差别的问题并不能解决当前的问题.

以前在2.1版中,此方法有效:

taskList.Add(MethodOne(myRequestObject));
taskList.Add(MethodTwo(myRequestObject));

await Task.WhenAll(taskList);

这两个方法仅从db上下文读取(从未更改),并且看起来像这样:

private async Task MethodOne(RequestObject myRequestObject)
{
    var entity = await DbContext
    .MyDbSet
    .OrderByDescending(x => x.SomeProperty)
    .FirstOrDefaultAsync(x => x.Id == myRequestObject.Id);

    if (entity != null)
        myRequestObject.SomeRequestProperty = entity.AnotherProperty;
    }
}

在3.1中,即使我只是在阅读而不更改实体,DB上下文ConcurrencyDetector也认为它具有EnterCriticalSection,当第二种方法试图在DbContext上等待时,它将导致异常:

InvalidOperationException:第二个操作在此上下文上开始 在上一个操作完成之前

出于基线的合理性考虑,我还尝试了以下可行的方法(但对于我的应用程序的真实代码而言,这不是理想的解决方案):

await MethodOne(myRequestObject);
await MethodTwo(myRequestObject);

这是我的问题:

  1. 是否有一种方法可以继续告诉EF core 3.1在我知道安全的情况下允许并发. 这在2.1中有效,因此不能简单地说db上下文从未允许这样做并将其作为一个重复项来结束,因此不能消除它.我的应用程序已经在生产中愉快地运行了很长时间.仅在迁移到3.1之后,这才成为问题.发生了什么变化?是否确实无法解决此更改,或者上下文是否不允许这种说法是否有例外?如果这在设计不允许"之前可行,那么现在是否同样有可能不正确呢?

解决方案

是否有一种方法可以继续告诉EF core 3.1在我知道安全的情况下允许并发.这项工作在2.1中很明显,因此不能仅仅通过说数据库上下文从未允许这样做并将其作为一个重复问题来结束就可以消除它.

数据库上下文从未允许过.我从未见过在任何版本的.NET Core上都可以使用这种代码.如果它发生在2.x中工作,那么它只是在代码正在赢得其竞争条件并因此很幸运的意义上起作用".性能上的任何更改-减少内存,防病毒软件,备用网络路由-都可能导致此操作失败.认识到代码错误一直都是非常重要.

我的应用很长时间以来一直在生产中愉快地运行.仅在迁移到3.1之后,这才成为问题.发生了什么变化?

可能是框架中的一些时间.

如果这在设计不允许"之前可行,那么现在是否同样不是真的?

比赛条件仍然存在.如果您的代码碰巧赢得了竞争条件,您将不会看到该异常.

此更改是否真的无法解决,或者上下文是否不允许这种说法是否有例外?

没有解决方法来强制dbcontext处理多个同时的请求.进行并发请求的正常模式是使用多个dbcontext,每个并发请求一个.

I'm migrating from netcoreapp 2.1 to 3.1, and I've found a breaking change for EF core 3.1 that I can't resolve. The following worked in 2.1 so simply saying db context isn't thread safe by design and pointing to other questions that don't deal with this nuance does not address the issue at hand.

Previously in 2.1, this worked:

taskList.Add(MethodOne(myRequestObject));
taskList.Add(MethodTwo(myRequestObject));

await Task.WhenAll(taskList);

where both methods only read(never changed) from the db context and look something like this:

private async Task MethodOne(RequestObject myRequestObject)
{
    var entity = await DbContext
    .MyDbSet
    .OrderByDescending(x => x.SomeProperty)
    .FirstOrDefaultAsync(x => x.Id == myRequestObject.Id);

    if (entity != null)
        myRequestObject.SomeRequestProperty = entity.AnotherProperty;
    }
}

In 3.1, even when I'm just reading and not changing the entities, the DB context ConcurrencyDetector thinks it has EnterCriticalSection which causes an exception when the second method tries to await on the DbContext:

InvalidOperationException: A second operation started on this context before a previous operation completed

For baseline sanity, I also tried the following which does work(but is not an ideal solution for the real code of my app):

await MethodOne(myRequestObject);
await MethodTwo(myRequestObject);

So here is my questions:

  1. Is there a way to continue to tell EF core 3.1 to allow concurrency when I know it is safe. this worked in 2.1 so clearly this cannot be dismissed by simply saying db context has never allowed this by design and closing the question as a duplicate. My app has been running happily in production for a long time. Only after migrating to 3.1 has this become an issue. What has changed? Is this change truly impossible to work around or are there exceptions to the claim that the context doesn't allow this? If this worked before when this 'wasn't allowed by design', is it possible that it is similarly not true now as well?

解决方案

Is there a way to continue to tell EF core 3.1 to allow concurrency when I know it is safe. this worked in 2.1 so clearly this cannot be dismissed by simply saying db context has never allowed this by design and closing the question as a duplicate.

The DB context has never allowed that. I've never seen this kind of code work on any version of .NET Core. If it happened to work in 2.x, it was only "working" in the sense that the code was winning its race conditions and thus just getting lucky. Any change in performance - reduced memory, antivirus software, alternate network routing - could have caused this to fail. It's very important to recognize that the code is wrong and always has been.

My app has been running happily in production for a long time. Only after migrating to 3.1 has this become an issue. What has changed?

Probably some timing in the framework.

If this worked before when this 'wasn't allowed by design', is it possible that it is similarly not true now as well?

The race condition still exists. If your code happens to win the race condition, you won't see that exception.

Is this change truly impossible to work around or are there exceptions to the claim that the context doesn't allow this?

There's no workaround to force a dbcontext to work with multiple simultaneous requests. The normal pattern for doing simultaneous requests is to use multiple dbcontexts, one for each simultaneous request.

这篇关于“第二个操作就此开始" EF核心3.1并发突破性变化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆