除非启用MARS,否则实体框架从6.1.x升级到6.2.0会破坏某些查询

时间:2017-11-09 17:54:25

标签: c# entity-framework linq

我最近在我们的一个大型项目上将EF 6.1.3升级到6.2.0,它已经打破了大量的LINQ查询。启用MultipleActiveResultSets会使所有内容再次正常工作,但我很难理解这一变化。我们多年来一直使用EF并且经历了多个主要版本更改而没有任何问题。如果我简单地回到6.1.3,一切都按预期工作 - 实际上即使我在6.1.3 中明确禁用MARS,一切都能正常工作。

让我举几个简单的例子。第一个问题是嵌套查询:

foreach(var row in dbSet.Where(<condition>))
    foreach(var innerRow in otherDbSet.Where(_ => _.Property == row.Property))

这在6.1.3中工作正常,但是在6.2.0中抛出了“已经有一个打开的DataReader ...”异常。我理解异常的本质,我可以通过在外部查询上调用ToList()来将结果推送到内存中来解决这个问题 - 我不明白为什么我不必在6.1.3中这样做(即使MARS禁用)。简单地将整个外部集合加载到存储器中并不总是令人满意的。

这似乎也会影响延迟加载的属性。例如,我们通过以下简单查询构建ComboBox:

return db.Collection
    .Where(<condition>)
    .AsEnumerable()
    .Select(_ => new ListItem(_.Id, _.LazyNavigationProperty.Description))
    .ToList();

这在6.1.3中运行良好,但在6.2.0中再次抛出“已经有一个打开的DataReader ...”异常。修复是我现在必须急于加载导航属性。

最终我没有明确的问题,我只是想了解为什么次要版本更新似乎会导致查询处理方式发生重大变化。

展望未来,这会对我们进行重构提出太多疑问。当我研究这个问题时,我看到了关于启用MARS的模糊警告,但没有人真正给出任何具体的东西。是否有令人信服的理由不启用它?

2 个答案:

答案 0 :(得分:1)

Entity Framework应该为您的数据库模型提供一个微小的抽象。

此类工作需要在后台执行多个查询。与手工编码的相同工作负载相比,引擎可能还需要更多必要的查询。

这是一种生理演变,以便能够处理所有可能的用户请求。只需升级到不同的Entity Framework版本,就可以引入不同的数据库工作负载。

MARS是必需的,因为EF更改了对象检索的执行方式(特别是在与延迟加载结合的循环内)。不幸的是,在大多数情况下,使用实体框架时需要使用MARS

现在,使用async/await通常也需要MARS

答案 1 :(得分:0)

之所以会出现此错误,是因为您在尝试打开另一个结果集时迭代了一个结果集(而第一个结果集尚未完成)->某种延迟加载(您情况下的第一个“ for each”迭代) )->有许多方法可以解决此问题,就像您自己亲眼所见:使用toList(首先放到内存中),因为不再使用数据读取器来打开集合。

它看起来可能与6.2中的错误修复(发行说明:https://entityframework.net/ef-version-history)有关-看起来与以下内容有关:“错误:重试查询或SQL命令失败,并且“另一个SqlParameter已包含在SqlParameter中SqlParameterCollection。”)

关于启用MARS: 您可以在此处找到特殊警告:

https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/sql/enabling-multiple-active-result-sets