【问题标题】:When should I use MultipleActiveResultSets=True when working with ASP.NET Core 3.0 and SQL Server 2019+?使用 ASP.NET Core 3.0 和 SQL Server 2019+ 时,我应该何时使用 MultipleActiveResultSets=True?
【发布时间】:2020-01-06 06:30:17
【问题描述】:

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

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

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

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

阅读此MS Docs article我看到应该注意各个方面,例如选项(ANSI_NULLSDATE_FORMATLANGUAGETEXTSIZE),安全上下文,当前数据库,状态变量(@987654329 @、@@ROWCOUNT@@FETCH_STATUS@@IDENTITY) 启用 MARS 时。

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

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

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

编辑以解决反馈

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

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

【问题讨论】:

  • 如果您执行需要 MARS 的操作,则应启用 MARS,否则将失败。您或其他人是否应该编写使用此类操作的代码是主观的。 SQL Server 开发人员看到了现有应用程序的需求并满足了它,尽管所有使用 MARS 的代码也可以编写为不使用它。甚至在 MARS 推出时也是如此,今天仍然如此。
  • @JeroenMostert - 通过“应该”我会理解“这是一个最佳实践”。我知道十年前使用 MARS 是有原因的,但考虑到当前的框架、软件架构和计算能力,考虑使用它是否有意义?或者很快,现在使用它是一个好习惯吗?
  • “良好做法”是“给我你(最好是受过教育的)意见”的另一种说法。我敢于提出一个客观的框架来评估 MARS 的使用与不使用,我怀疑这样的事情是否适合对 SO 的单一答案——它显然不够好或不够坏为了那个原因。当然,虽然我不介意被证明是错误的。

标签: sql-server asp.net-core entity-framework-core sql-server-mars


【解决方案1】:

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


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

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

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

基本上有两种解决方案。

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

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

听起来不错,不是吗?有一个小而重要的警告 - 它需要 MARS!否则,它必须切换到缓冲模式。这完全违背了IEnumerator 和异步版本的想法——取消概念。您可以在我对How can I abort a running EF Core Query using GetAsyncEnumerator? 的回答中看到后者的效果,最后建议是启用 MARS。

有关 EF Core 查询的更多信息,请参阅官方 EF Core 文档的 Split queriesHow Queries Work(基本上是整个 Query data)部分。

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


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

【讨论】:

  • 感谢展示如何使用 MARS 的示例。到目前为止,我可以在不需要 GetAsyncEnumerator 的情况下读取所有需要的数据(主数据 + 相关数据),所以我从来没有觉得需要像 MARS 这样的东西。我认为 MARS 默认处于禁用状态,因为它需要对正在发生的事情有更深入的了解,这不是真正的并行性,如 this article 所示。
  • @Alexei-checkCodidact 与并行无关。无非是同时提供几个打开的游标。同样,我相信 T-SQL 内部支持的东西,所以只需要暴露给客户端 API。 SqlServer 人多次设置一些非逻辑约束,著名的是“循环或多级联路径”,其他数据库提供开箱即用的 w/o 问题,因此它们绝对是可行的。但事实就是这样:-)
  • EF Core 5 introducing support for savepoints 开始,EF 会在启用 MARS 时生成警告,说明它不能与保存点一起使用。因此,我犹豫是否应该盲目启用 MARS。相反,我现在倾向于禁用 MARS,除非我知道我需要它并理解权衡(坦率地说,我现在不知道)。
猜你喜欢
  • 1970-01-01
  • 2020-04-11
  • 2021-11-10
  • 2020-02-25
  • 1970-01-01
  • 2019-10-10
  • 2020-10-02
  • 1970-01-01
  • 2012-08-05
相关资源
最近更新 更多