SqlClient 中的 AppContext 开关

适用于 .NET Framework .NET .NET Standard

下载 ADO.NET

AppContext 类允许 SqlClient 提供新的功能,同时继续支持依赖于先前行为的调用方。 用户可通过设置特定的 AppContext 开关来选择退出一种行为更改。

SqlClient 在首次使用该交换机时读取并缓存该交换机。 在应用程序启动时,在使用任何 SqlClient 类型之前设置这些开关。 在 SqlClient 已缓存其值之后更改开关,不会产生任何影响。

默认启用 MultiSubnetFailover

适用于:.NET Framework;.NET;.NET Standard

(从版本 7.0 开始可用)

要在不修改单个连接字符串的情况下设置 MultiSubnetFailover=true 全局,请在应用启动时将 AppContext 切换 Switch.Microsoft.Data.SqlClient.EnableMultiSubnetFailoverByDefault 设置为 true

AppContext.SetSwitch("Switch.Microsoft.Data.SqlClient.EnableMultiSubnetFailoverByDefault", true);

还可以在 App.Config 中启用此开关:

<runtime>
  <AppContextSwitchOverrides value="Switch.Microsoft.Data.SqlClient.EnableMultiSubnetFailoverByDefault=true" />
</runtime>

启用后,所有连接的行为就像连接字符串中设置了 MultiSubnetFailover=true 参数一样。 默认情况下,此开关处于禁用状态。

为异步读取启用数据包复用

适用于:.NET Framework;.NET;.NET Standard

(从版本 7.0 开始可用)

数据包多路复用可提高大型异步读取操作的性能,例如 ExecuteReaderAsync 使用大型结果集、流式处理方案或批量数据检索。 此功能由两个选择加入 AppContext 开关控制。 将两个开关设置为 false 启用新的异步处理路径:

AppContext.SetSwitch("Switch.Microsoft.Data.SqlClient.UseCompatibilityAsyncBehaviour", false);
AppContext.SetSwitch("Switch.Microsoft.Data.SqlClient.UseCompatibilityProcessSni", false);

默认情况下,这两个开关都保留在true位置,从而保留现有(兼容)的行为。

启用用户代理功能扩展

适用于:.NET Framework;.NET;.NET Standard

(从版本 7.0 开始可用)

启用 AppContext 开关 Switch.Microsoft.Data.SqlClient.EnableUserAgent 时,驱动程序会作为连接的一部分向服务器发送用户代理详细信息。 此信息有助于按版本和操作系统对驱动程序使用情况进行故障排除和量化。 默认情况下,此开关处于禁用状态。 若要启用它,请在应用程序启动时将 AppContext 开关设置为 true

AppContext.SetSwitch("Switch.Microsoft.Data.SqlClient.EnableUserAgent", true);

启用十进制截断行为

适用于:.NET Framework;.NET;.NET Standard

从 Microsoft.Data.SqlClient 2.0 开始,与 SQL Server 一样,小数数据默认四舍五入。 要启用先前的截断行为,可以在应用程序启动时将 AppContext 开关 Switch.Microsoft.Data.SqlClient.TruncateScaledDecimal 设置为 true

AppContext.SetSwitch("Switch.Microsoft.Data.SqlClient.TruncateScaledDecimal", true);

在 Windows 上启用托管网络

适用于:.NET; .NET Standard

(仅从版本 2.0 开始可用)

在 Windows 上,SqlClient 默认使用 SNI 网络接口的本机实现。 要启用托管 SNI 实现,请在应用程序启动时将 AppContext 开关 Switch.Microsoft.Data.SqlClient.UseManagedNetworkingOnWindows 设置为 true

AppContext.SetSwitch("Switch.Microsoft.Data.SqlClient.UseManagedNetworkingOnWindows", true);

此开关会切换驱动程序的行为,以便在 Windows 上的 .NET Core 2.1+ 和 .NET Standard 2.0+ 项目中使用托管的网络实现,从而消除对 Microsoft.Data.SqlClient 库的本机库的所有依赖项。 仅用于测试和调试目的。

注意

与本机实现相比,两者存在一些已知的差异。 例如,托管实现不支持非域的 Windows 认证。

禁用透明网络IP解析

适用于:.NET Framework

透明网络 IP 解析 (TNIR) 是对现有 MultiSubnetFailover 功能的修订。 如果第一个解析的主机名 IP 未响应,且存在多个与主机名关联的 IP,TNIR 就会影响驱动程序的连接序列。 TransparentNetworkIPResolutionMultiSubnetFailover的组合选择连接顺序:

透明网络IP解析 MultiSubnetFailover 连接顺序
True True TransparentNetworkIPResolution 将被忽略。 驱动程序并行尝试DNS解析的IP地址,并与第一个响应者完成认证。
True 驱动程序会针对 DNS 解析得到的各个 IP 地址进行多轮连接尝试:首次尝试的超时时间至少为 500 毫秒,后续每次尝试的超时时间会逐步增大,直到连接成功或达到总体 Connect Timeout
True 驱动程序并行尝试DNS解析的IP地址,并与第一个响应者完成认证。
驱动程序会依次尝试 DNS 解析得到的每个 IP 地址,直到其中一个成功或达到 Connect Timeout

在 .NET Framework 上,TransparentNetworkIPResolution 默认启用,而 MultiSubnetFailover 默认禁用。 在 .NET 5 及以后版本中,TransparentNetworkIPResolution没有识别的连接字符串关键字,设置它(任意值)都会抛ArgumentException出 (KeywordNotSupported)。 这些版本仅遵循 MultiSubnetFailover。 本节其余部分(自动覆盖、以下警告中的故障模式以及 AppContext 切换)适用于 .NET 框架。

小窍门

在每个连接字符串上设置MultiSubnetFailover=True,无论.NET版本如何,或目标是Azure SQL还是本地SQL Server。 MultiSubnetFailover=True 选择一条并行连接代码路径,快速找到第一个响应式副本。 在 .NET Framework 上,它还会绕过 TNIR 按 IP 顺序逐个重试的循环,而这正是导致连接延迟过长和预身份验证握手超时的常见原因。

在 .NET Framework 中,当连接字符串中未指定 TransparentNetworkIPResolution 时,如果数据源是已识别的 Azure SQL 终结点,或者将 Authentication 键设置为任一 Microsoft Entra ID 方法(Active Directory PasswordActive Directory IntegratedActive Directory InteractiveActive Directory Service PrincipalActive Directory Device Code FlowActive Directory Managed IdentityActive Directory MSIActive Directory DefaultActive Directory Workload Identity),或者设置了 SqlConnection.AccessToken 属性,驱动程序会自动禁用 TNIR。 关于驱动程序识别的端点后缀,请参见 TransparentNetworkIPResolutionSqlConnection.ConnectionString 中的条目。

显式值绕过了这种自动行为:TransparentNetworkIPResolution启用TrueTNIR,False无条件禁用TNIR。 要恢复自动行为,请从连接字符串中移除关键词。 当连接字符串通过自定义 CNAME 或个性化 DNS 名称指向 Azure SQL,且其后缀未被识别为 Azure SQL 终结点时,自动替代同样不适用。 该自动替代机制专门针对 Azure SQL;它不会在本地部署的 SQL Server 上生效,因此 TNIR 在那里默认处于启用状态。

.NET框架上的长连接延迟

在 .NET 框架 TransparentNetworkIPResolution=True (默认设置)中,当目标 DNS 名称解析为多个 IP 且其中一个早期 IP 不健康、过时或无法访问时,可能会导致长时间连接延迟和认证前握手超时。 TNIR 会依次尝试已解析出的 IP 地址,并在每一轮增加单次尝试的超时时间,直到达到总体 Connect Timeout 限制。 你通常会观察到一个意外长的连接延迟,最终导致以下错误:

Connection Timeout Expired.  The timeout period elapsed while attempting to consume the pre-authentication handshake acknowledgement.  This could be because the pre-authentication handshake failed or the server was unable to respond back in time.

该模式在几种拓扑中表现出来:

  • Azure SQL 数据库、Azure SQL 托管实例,或 Microsoft Fabric 中的 SQL 数据库。 Azure SQL 网关将每个认证路由到后端副本。 当路由连接失败时,TNIR 会重试已路由到的后端,而不返回网关重新路由,这会延长后端故障切换期间的延迟。
  • 本地的 SQL Server 位于 Always On 可用性组监听器后面,该 DNS 名称解析为多个副本 IP。 在 TNIR 找到可用副本之前,会依次尝试过期的 DNS 记录或不健康的副本 IP 地址。
  • 带有多子网集群监听器的故障切换集群实例,或任何目标DNS名称包含多个 A/AAAA 记录的配置(如DNS轮询)。

为避免此行为,在 连接字符串 中设置MultiSubnetFailover=True

MultiSubnetFailover=True

该建议适用于所有 .NET 版本,涵盖 Azure SQL 和本地 SQL Server。 当 MultiSubnetFailover=True时,驱动程序会忽略 TransparentNetworkIPResolution,并并行尝试 DNS 解析得到的各个 IP 地址,使用第一个作出响应的副本完成身份验证。 尽管名称如此,MultiSubnetFailover 适用于其 DNS 名称解析为多个目标 IP 地址的任何侦听器,无论这些 IP 地址是否位于不同的子网中;而且对于 DNS 解析为单个 IP 地址的独立服务器,它也是安全的。

若要在不编辑每个连接字符串的情况下对整个进程进行控制,请使用 默认启用 MultiSubnetFailover AppContext 开关。

用AppContext开关禁用TNIR

要在 .NET Framework 上将 TransparentNetworkIPResolution 的默认值从 true 更改为 false,请在应用程序启动时将 AppContext 开关 Switch.Microsoft.Data.SqlClient.DisableTNIRByDefaultInConnectionString 设置为 true。 该开关仅在连接字符串中不包含 TransparentNetworkIPResolution 时更改默认值;它不会覆盖显式指定的值。

AppContext.SetSwitch("Switch.Microsoft.Data.SqlClient.DisableTNIRByDefaultInConnectionString", true);

有关设置这些属性的详细信息,请参阅 SqlConnection.ConnectionString 属性文档。

在登录期间启用最小超时

适用于:.NET Framework;.NET;.NET Standard

为了防止登录尝试无限期地等待,可以在应用程序启动时将 AppContext 开关 Switch.Microsoft.Data.SqlClient.UseOneSecFloorInTimeoutCalculationDuringLogin 设置为 true

AppContext.SetSwitch("Switch.Microsoft.Data.SqlClient.UseOneSecFloorInTimeoutCalculationDuringLogin", false);

禁用 ReadAsync 的阻止行为

适用于:.NET Framework;.NET;.NET Standard

从 3.0 版本开始,ReadAsync 以异步方式运行。 以前的版本在 .NET Framework 上会同步运行 ReadAsync,并阻塞调用线程。 为了控制这种阻塞行为,请在应用启动时将 AppContext 切换Switch.Microsoft.Data.SqlClient.MakeReadAsyncBlocking设置为truefalse或:

AppContext.SetSwitch("Switch.Microsoft.Data.SqlClient.MakeReadAsyncBlocking", false);

启用 rowversion 的 NULL 行为

适用于:.NET Framework;.NET;.NET Standard

从3.0版本开始,当 行版本 的空值时,返回 SqlDataReader 的是 DBNull 值而不是空 byte[]值。 要启用返回空 byte[]的遗留行为,请在应用启动时启用 AppContext 开关 Switch.Microsoft.Data.SqlClient.LegacyRowVersionNullBehavior

AppContext.SetSwitch("Switch.Microsoft.Data.SqlClient.LegacyRowVersionNullBehavior", true);

禁止显示不安全的 TLS 警告

适用于:.NET Framework;.NET;.NET Standard

(从版本 4.0.1 开始可用)

在 连接字符串 中使用Encrypt=false时,如果 TLS 版本为 1.2 或更低,控制台会输出安全警告。 通过在应用启动时启用以下 AppContext 开关来抑制此警告:

AppContext.SetSwitch("Switch.Microsoft.Data.SqlClient.SuppressInsecureTLSWarning", true);

忽略服务器指定的故障切换伙伴

适用于:.NET Framework;.NET;.NET Standard

(从版本 5.1.8、6.0.4 和 6.1.3 开始可用)

故障转移后,服务器提供的故障转移伙伴信息优先于连接字符串中提供的故障转移伙伴信息。 若要忽略服务器提供的故障转移伙伴信息,并且只考虑连接字符串中提供的故障转移伙伴信息,请启用应用程序启动时的此 AppContext 开关:

AppContext.SetSwitch("Switch.Microsoft.Data.SqlClient.IgnoreServerProvidedFailoverPartner", true);

强制执行连接空闲超时

适用于:.NET Framework;.NET;.NET Standard

从版本 7.1.0-preview2 开始,Connection Idle Timeout连接字符串 关键字将空闲时长配置为秒数,之后池化连接有资格被逐出(默认 300;值为 0 则禁用空闲过期)。 符合条件的连接会在后续检索或维护过程中被丢弃,因此确切时间可能因池实现和维护频率而异。 该关键字仅在禁用旧版空闲超时行为时生效。 当该开关的默认值为 true 时,该池将保留历史行为,并且该关键字不起作用。

AppContext.SetSwitch("Switch.Microsoft.Data.SqlClient.UseLegacyIdleTimeoutBehavior", false);

启用V2连接池

适用于:.NET Framework;.NET;.NET Standard

从6.1版本开始,SqlClient包含了一个替代的实验性连接池实现(V2)。 V1 池仍然是默认(交换机默认为 false)。 要选择加入 V2 池,应用启动时启用 AppContext 切换 Switch.Microsoft.Data.SqlClient.UseConnectionPoolV2

AppContext.SetSwitch("Switch.Microsoft.Data.SqlClient.UseConnectionPoolV2", true);

将连接池等待时间计入连接超时时间

适用于:.NET Framework;.NET;.NET Standard

从 7.1.0-preview2 版本开始,等待从连接池获取连接的时间可能会计入调用方的 Connect Timeout 预算,因此,等待连接池分配连接和网络连接尝试共用同一个总超时时间。 当开关设置为其默认值 false 时,池操作会获得完整的 Connect Timeout,而网络连接尝试还会另外获得一整份完整的预算。

AppContext.SetSwitch("Switch.Microsoft.Data.SqlClient.UseOverallConnectTimeoutForPoolWait", true);

发生登录错误时恢复为旧版故障转移交替方式

适用于:.NET Framework;.NET;.NET Standard

从版本 7.1.0-preview2 开始,在配置了故障转移的情况下进行连接时,对于登录阶段返回的 SQL 错误,SqlClient 不再切换到故障转移伙伴。 要恢复到遗留交替行为,请在应用启动时启用 AppContext 切换 Switch.Microsoft.Data.SqlClient.UseLegacyFailoverAlternationOnLoginSqlErrors 。 开关默认为 false

AppContext.SetSwitch("Switch.Microsoft.Data.SqlClient.UseLegacyFailoverAlternationOnLoginSqlErrors", true);

遵循 vartime 参数上显式指定的零小数位数

适用于:.NET Framework;.NET;.NET Standard

默认情况下,当你显式地将 datetime2datetimeoffsettime 参数的刻度设置为 0 时,SqlClient 会发送一个 7 的刻度。 在 6.0 或更高版本中,请在应用程序启动时将 Switch.Microsoft.Data.SqlClient.LegacyVarTimeZeroScaleBehaviour 设置为 false,以保留显式的 0 缩放比例。 开关默认为 true

AppContext.SetSwitch("Switch.Microsoft.Data.SqlClient.LegacyVarTimeZeroScaleBehaviour", false);