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

amn*_*sia 20 c# linq entity-framework

我最近在我们的一个大型项目上将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))
Run Code Online (Sandbox Code Playgroud)

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

这似乎也会影响延迟加载的属性.例如,我们从简单的查询构建ComboBoxes,如下所示:

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

这在6.1.3中工作正常,但在6.2.0中再次抛出"已经有一个打开的DataReader ..."异常.修复是我现在必须急于加载导航属性.

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

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

小智 4

您收到此错误是因为您在尝试打开另一个结果集时迭代结果集(而第一个结果集尚未完成)-> 某种延迟加载(在您的情况下是第一个“for every”迭代)->有很多方法可以解决这个问题,正如您已经亲眼所见:使用 toList (首先放入内存),因为它不再使用 datareader 来打开集合。

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

关于启用 MARS:您可以在这里找到特别警告:

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