11

我编写的大多数应用程序都不使用MultipleActiveResultSets=True,但我已经看到在其中几个和一些教程中启用了该选项。

这个SO question涉及相同的主题,但它已经很老了,我相信同时情况发生了很大变化。

OP 争论执行一些非查询,同时执行ExecuteReader. 在这种情况下,我认为这是一个糟糕的设计,因为它可能会被一些批处理式操作所取代,也许是一个存储过程,以最大限度地减少往返次数。

当将 Entity Framework 与 ASP.NET Core 一起使用并接收到与数据上下文相关的异常时,我将其视为一个错误,而不是考虑启用 MARS。

阅读这篇MS Docs 文章,我发现在启用 MARS 时应该注意各个方面,例如选项(ANSI_NULLSDATE_FORMATLANGUAGETEXTSIZE)、安全上下文、当前数据库、状态变量(@@ERROR@@ROWCOUNT@@FETCH_STATUS、 )。@@IDENTITY

此外,10 年以上意味着功能更强大的服务器能够在真正需要时保持更多连接(缓存应该有助于减少这种需求)。

所以我想知道在使用现代 ASP.NET Core 应用程序(3.0+)时是否必须考虑启用 MARS。

问题:在使用 ASP.NET Core 3.0 和 SQL Server 2019+ 时,我应该何时使用 MultipleActiveResultSets=True?

编辑以解决反馈

我对详尽的分析不感兴趣,但有几个适当的上下文来证明使用 MARS 是否合理。

ASP.NET Core 应用程序中的一个典型示例是将数据库上下文设置为范围(每个请求从连接池获取数据库连接,进行更改,通常每个请求/范围一个事务)。到目前为止,我已将与每个连接的多个查询相关的错误视为我自己的错误以避免 MARS,但我这样做并没有真正理解原因。

4

1 回答 1

19

是的,MARS 在现代数据访问框架中仍然占有一席之地,因为它们提供了以下两个主要一般查询问题的(有效)解决方案 - 流式传输(即非缓冲)(1)具有急切加载的相关数据集合的数据和(2)惰性加载任何类型的相关数据。


在这两种情况下,执行查询都应提供IEnumerator<T>(或其异步版本),它是数据读取器(或数据库转发只读游标)的对象等价物。因此,每个都MoveNext{Async}应该映射到ReadNext数据读取器,并且预计会提供一个完全填充的数据,而不会T在所有其他数据之前缓冲。为了实现这一点,底层数据读取器必须在枚举期间保持打开状态,并在完成或提前中止时关闭(例如,FirstOrDefault()) - 原因之一IEnumerator<T>IDisposable

现在想象一下如果你启用了延迟加载会发生什么。你得到一些实体并访问一些导航属性。这会触发延迟加载操作,这当然需要执行 reader 从数据库中获取数据。由于外部阅读器仍处于打开状态(活动),因此如果没有 MARS,此操作将简单地因运行时异常而失败。不好。除了提前缓冲所有内容(基本上切换到快照模式)或不使用延迟加载之外,您或框架无能为力。

假设您不使用延迟加载(无论如何都不推荐)。但是您的实体包含相关的数据集合,并且您希望预先加载它们。关系数据库 SQL 提供平面结果集,即不支持查询结果集中的“嵌套集合”。那么如何流式传输这些数据呢?

基本上有两种解决方案。

首先是基于单个 SQL 查询,该查询包含所有主表 + 相关表列,并返回某种混合记录,其中某些字段适用于特定结果,其他字段为空。EF6 和 EF Core 3.0+ 使用这种方法。EF Core 1.x/2.x 使用另一种方法,而 EF Core 5.0 允许您在两者之间进行选择。为什么?因为当您有多个子集合时,这往往会产生非常无效的查询(执行和处理结果集,因为它传输了大量不必要的数据)。

其次是使用单独的查询 - 一个用于主要结果集,一个用于每个相关的子集合。这个想法很简单。由于通常 PK 和 FK 都被索引,数据库可以使用索引有效地返回按这些列排序的它们(join无论如何操作都需要),然后可以通过提前读取(缓冲)最多一条记录来轻松地在客户端合并它们。

听起来不错,不是吗?有一个小而重要的警告 - 它需要 MARS!否则,它必须切换到缓冲模式。这完全违背了IEnumerator异步版本的想法 - 取消概念。您可以在我对如何使用 GetAsyncEnumerator 中止正在运行的 EF Core 查询的回答中看到后者的效果?,最后的建议是启用 MARS。

有关 EF Core 查询的详细信息,请参阅官方 EF Core 文档的拆分查询查询工作方式(基本上是整个查询数据)部分。

旁注单独的连接并不是一个真正的选择,特别是如果需要像可重复读取这样的事务级别。MARS 提供了连接所需的精确抽象。并且 SP 中的 AFAIK 可以同时打开任意数量的游标,因此不确定 ADO 连接层的确切问题是什么,以及为什么 MARS 被认为是需要启用的可选功能,而不仅仅是开箱即用的功能。ORM 虽然可以尝试在适用的情况下在后台使用单独的连接。EF Core 目前没有。


因此,简要回顾一下,如果您不使用延迟加载(可能)并且没有相关集合(不太可能 - 一对多很常见,并且相关集合并不一定意味着导航属性和Include- 同样适用到列表和类似成员的预测),那么你不需要 MARS。否则最好启用它们 - 它们是功能,所以使用它。

于 2021-04-10T17:19:51.890 回答