我编程的大多数应用程序都不使用MultipleActiveResultSets=True
,但我在几个应用程序和一些教程中看到该选项被启用。
这个SO问题涉及相同的主题,但它非常古老,我相信与此同时情况发生了很大变化。
OP 争论在执行ExecuteReader
时执行一些非查询。在这种情况下,我认为这是一个糟糕的设计,因为它可能会被一些批处理式操作所取代,也许是一个存储过程,以尽量减少往返次数。
将实体框架与 ASP.NET Core 一起使用并收到与已在范围内执行某些内容的数据上下文相关的异常时,我将其视为 bug,而不考虑启用 MARS。
阅读这篇 MS Docs 文章,我看到在使用启用 MARS 时,应该注意各个方面,例如选项(ANSI_NULLS
、DATE_FORMAT
、LANGUAGE
、TEXTSIZE
)、安全上下文、当前数据库、状态变量(@@ERROR
、@@ROWCOUNT
、@@FETCH_STATUS
、@@IDENTITY
)。
此外,10 +年意味着如果确实需要,功能更强大的服务器能够容纳更多的连接(缓存应该有助于减少这种需求)。
所以我想知道在使用现代 ASP.NET 核心应用程序 (3.0+) 时是否必须考虑启用 MARS。
问:在使用 Core 3.0 和 SQL Server 2019+ 时,何时应使用 MultipleActiveResultSets=True ASP.NET?
编辑以解决反馈
我对详尽的分析不感兴趣,但对一些适当的上下文来证明是否使用 MARS 不感兴趣。
ASP.NET Core 应用程序中的一个典型示例是将数据库上下文作为作用域(每个请求从连接池获取数据库连接,进行更改,通常每个请求/作用域一个事务)。到目前为止,我已经将与每个连接的多个查询相关的错误视为我自己的错误,以避免 MARS,但我这样做时并没有真正了解原因。
是的,MARS 在现代数据访问框架中仍然占有一席之地,因为它们为以下两个主要的常规查询问题提供了(有效的)解决方案 - 流式处理(即非缓冲)(1) 具有预先加载的相关数据收集的数据和 (2) 任何类型的延迟加载相关数据。
在这两种情况下,执行查询都应提供IEnumerator<T>
(或其异步版本),这是数据读取器(或数据库仅转发只读游标)的对象等效项。因此,每个MoveNext{Async}
都应该映射到数据读取器ReadNext
,并期望提供一个完全填充的T
,而不缓冲所有其他。为了实现这一点,基础数据读取器必须在枚举期间保持打开状态,并在枚举完成或更早中止时关闭(例如,FirstOrDefault()
) -IEnumerator<T>
的原因之一是IDisposable
。
现在想象一下,如果您启用了延迟加载会发生什么。您获得一些实体并访问一些导航属性。这会触发延迟加载操作,这当然需要执行 reader 才能从数据库中获取数据。由于外部读取器仍处于打开状态(活动状态),因此没有 MARS 此操作将失败并出现运行时异常。不好。除了提前缓冲所有内容(基本上切换到快照模式)或不使用延迟加载之外,您或框架无能为力。
假设您不使用延迟加载(无论如何不建议这样做)。但是您的实体包含相关的数据集合,并且您希望预先加载它们。关系数据库 SQL 提供平面结果集,即不支持查询结果集中的"嵌套集合"。那么如何流式传输此类数据呢?
基本上有两种解决方案。
首先是基于包含所有主 + 相关表列的单个 SQL 查询,并返回某种混合记录,其中某些字段适用于特定结果,而其他字段为 null。此方法由 EF6 和 EF Core 3.0+ 使用。EF Core 1.x/2.x 使用另一种方法,而 EF Core 5.0 允许你在两者之间进行选择。为什么?因为当您有多个子集合时,这往往会产生非常低效的查询(执行和处理结果集,因为它传输大量不必要的数据)。
其次是使用单独的查询 - 一个用于主结果集,一个用于每个相关的子集合。这个想法很简单。由于通常 PK 和 FK 都被索引,因此数据库可以使用 index(无论如何join
操作都需要)有效地返回按这些列排序的它们,然后可以通过提前读取(缓冲)最多一条记录来轻松合并客户端。
听起来不错,不是吗?有一个小但重要的警告 - 它需要 MARS! 否则,它必须切换到缓冲模式。这完全违背了IEnumerator
和异步版本的想法 - 取消概念。您可以在我回答如何使用 GetAsyncEnumerator 中止正在运行的 EF Core 查询?中看到后者的效果,最后建议启用 MARS。
有关 EF Core 查询的详细信息,请参阅官方 EF Core 文档的拆分查询和查询工作原理(以及基本上是整个查询数据)部分。
旁注单独连接实际上不是一种选择,特别是当需要可重复读取等事务级别时。MARS 提供了连接所需的确切抽象。SP内部的AFAIK可以同时打开任意数量的光标,因此不确定ADO连接层的确切问题是什么,以及为什么MARS被认为是需要启用的可选功能,而不仅仅是开箱即用的功能。不过,ORM可以在适用的情况下尝试在幕后使用单独的连接。EF Core 目前没有。
所以简短地回顾一下,如果你不使用延迟加载(可能)并且没有相关集合(不太可能 - 一对多很常见,相关集合不一定意味着导航属性和Include
- 同样适用于列表和类似成员的投影),那么你不需要 MARS。否则最好启用它们 - 它们是功能,所以使用它。