From b7444d6de6fa1234314dc69cd4d3a9faef0ea8ed Mon Sep 17 00:00:00 2001
From: Paul Medynski <31868385+paulmedynski@users.noreply.github.com>
Date: Wed, 17 Dec 2025 18:28:13 -0400
Subject: [PATCH 1/7] - Eliminated race conditions when using the
LocalAppContextSwitchesHelper. - Removed Tristate in favour of bool?. - Gave
the Common test project access to MDS internals and cleaned up the Helper a
lot.
---
.../src/Microsoft.Data.SqlClient.csproj | 1 +
.../netfx/src/Microsoft.Data.SqlClient.csproj | 3 +
.../Data/SqlClient/LocalAppContextSwitches.cs | 806 +++++++++---------
.../tests/Common/Common.csproj | 1 +
.../Common/Fixtures/CertificateFixtureBase.cs | 4 +-
.../ColumnEncryptionCertificateFixture.cs | 20 +-
.../ColumnMasterKeyCertificateFixture.cs | 2 +-
.../Common/Fixtures/CspCertificateFixture.cs | 8 +-
.../Common/LocalAppContextSwitchesHelper.cs | 549 +++---------
.../tests/FunctionalTests/SqlParameterTest.cs | 6 +-
.../SQL/DataReaderTest/DataReaderTest.cs | 4 +-
.../SQL/ParameterTest/ParametersTest.cs | 6 +-
.../Data/SqlClient/SqlConnectionStringTest.cs | 60 +-
.../ConnectionFailoverTests.cs | 2 +-
.../SimulatedServerTests/ConnectionTests.cs | 4 +-
15 files changed, 594 insertions(+), 882 deletions(-)
diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj
index f8fc03f915..512db7418e 100644
--- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj
+++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj
@@ -32,6 +32,7 @@
$([System.IO.Path]::Combine('$(IntermediateOutputPath)','$(TargetFramework)','$(TargetFrameworkMoniker).AssemblyAttributes$(DefaultLanguageSourceExtension)'))
+
diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj
index e0974d0d44..bc14696009 100644
--- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj
+++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj
@@ -33,6 +33,9 @@
<_Parameter1>UnitTests
+
+ <_Parameter1>Common
+
diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/LocalAppContextSwitches.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/LocalAppContextSwitches.cs
index 09d84e0ea7..b1441db382 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/LocalAppContextSwitches.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/LocalAppContextSwitches.cs
@@ -4,540 +4,534 @@
using System;
-namespace Microsoft.Data.SqlClient
-{
- internal static partial class LocalAppContextSwitches
- {
- // @TODO: Replace with `bool?` since that's exactly how this is being used
- private enum Tristate : byte
- {
- NotInitialized = 0,
- False = 1,
- True = 2
- }
+#nullable enable
- private const string MakeReadAsyncBlockingString = @"Switch.Microsoft.Data.SqlClient.MakeReadAsyncBlocking";
- private const string LegacyRowVersionNullString = @"Switch.Microsoft.Data.SqlClient.LegacyRowVersionNullBehavior";
- private const string SuppressInsecureTlsWarningString = @"Switch.Microsoft.Data.SqlClient.SuppressInsecureTLSWarning";
- private const string UseMinimumLoginTimeoutString = @"Switch.Microsoft.Data.SqlClient.UseOneSecFloorInTimeoutCalculationDuringLogin";
- private const string LegacyVarTimeZeroScaleBehaviourString = @"Switch.Microsoft.Data.SqlClient.LegacyVarTimeZeroScaleBehaviour";
- private const string UseCompatibilityProcessSniString = @"Switch.Microsoft.Data.SqlClient.UseCompatibilityProcessSni";
- private const string UseCompatibilityAsyncBehaviourString = @"Switch.Microsoft.Data.SqlClient.UseCompatibilityAsyncBehaviour";
- private const string UseConnectionPoolV2String = @"Switch.Microsoft.Data.SqlClient.UseConnectionPoolV2";
- private const string TruncateScaledDecimalString = @"Switch.Microsoft.Data.SqlClient.TruncateScaledDecimal";
- private const string IgnoreServerProvidedFailoverPartnerString = @"Switch.Microsoft.Data.SqlClient.IgnoreServerProvidedFailoverPartner";
- private const string EnableUserAgentString = @"Switch.Microsoft.Data.SqlClient.EnableUserAgent";
- private const string EnableMultiSubnetFailoverByDefaultString = @"Switch.Microsoft.Data.SqlClient.EnableMultiSubnetFailoverByDefault";
+namespace Microsoft.Data.SqlClient;
- #if NET
- private const string GlobalizationInvariantModeString = @"System.Globalization.Invariant";
- private const string GlobalizationInvariantModeEnvironmentVariable = "DOTNET_SYSTEM_GLOBALIZATION_INVARIANT";
+internal static class LocalAppContextSwitches
+{
+ private const string MakeReadAsyncBlockingString = @"Switch.Microsoft.Data.SqlClient.MakeReadAsyncBlocking";
+ private const string LegacyRowVersionNullString = @"Switch.Microsoft.Data.SqlClient.LegacyRowVersionNullBehavior";
+ private const string SuppressInsecureTlsWarningString = @"Switch.Microsoft.Data.SqlClient.SuppressInsecureTLSWarning";
+ private const string UseMinimumLoginTimeoutString = @"Switch.Microsoft.Data.SqlClient.UseOneSecFloorInTimeoutCalculationDuringLogin";
+ private const string LegacyVarTimeZeroScaleBehaviourString = @"Switch.Microsoft.Data.SqlClient.LegacyVarTimeZeroScaleBehaviour";
+ private const string UseCompatibilityProcessSniString = @"Switch.Microsoft.Data.SqlClient.UseCompatibilityProcessSni";
+ private const string UseCompatibilityAsyncBehaviourString = @"Switch.Microsoft.Data.SqlClient.UseCompatibilityAsyncBehaviour";
+ private const string UseConnectionPoolV2String = @"Switch.Microsoft.Data.SqlClient.UseConnectionPoolV2";
+ private const string TruncateScaledDecimalString = @"Switch.Microsoft.Data.SqlClient.TruncateScaledDecimal";
+ private const string IgnoreServerProvidedFailoverPartnerString = @"Switch.Microsoft.Data.SqlClient.IgnoreServerProvidedFailoverPartner";
+ private const string EnableUserAgentString = @"Switch.Microsoft.Data.SqlClient.EnableUserAgent";
+ private const string EnableMultiSubnetFailoverByDefaultString = @"Switch.Microsoft.Data.SqlClient.EnableMultiSubnetFailoverByDefault";
- #if _WINDOWS
- private const string UseManagedNetworkingOnWindowsString = "Switch.Microsoft.Data.SqlClient.UseManagedNetworkingOnWindows";
- #endif
- #else
- private const string DisableTnirByDefaultString = @"Switch.Microsoft.Data.SqlClient.DisableTNIRByDefaultInConnectionString";
- #endif
+ #if NET
+ private const string GlobalizationInvariantModeString = @"System.Globalization.Invariant";
+ private const string GlobalizationInvariantModeEnvironmentVariable = "DOTNET_SYSTEM_GLOBALIZATION_INVARIANT";
- // this field is accessed through reflection in tests and should not be renamed or have the type changed without refactoring NullRow related tests
- private static Tristate s_legacyRowVersionNullBehavior;
- private static Tristate s_suppressInsecureTlsWarning;
- private static Tristate s_makeReadAsyncBlocking;
- private static Tristate s_useMinimumLoginTimeout;
- // this field is accessed through reflection in Microsoft.Data.SqlClient.Tests.SqlParameterTests and should not be renamed or have the type changed without refactoring related tests
- private static Tristate s_legacyVarTimeZeroScaleBehaviour;
- private static Tristate s_useCompatibilityProcessSni;
- private static Tristate s_useCompatibilityAsyncBehaviour;
- private static Tristate s_useConnectionPoolV2;
- private static Tristate s_truncateScaledDecimal;
- private static Tristate s_ignoreServerProvidedFailoverPartner;
- private static Tristate s_enableUserAgent;
- private static Tristate s_multiSubnetFailoverByDefault;
+ #if _WINDOWS
+ private const string UseManagedNetworkingOnWindowsString = "Switch.Microsoft.Data.SqlClient.UseManagedNetworkingOnWindows";
+ #endif
+ #else
+ private const string DisableTnirByDefaultString = @"Switch.Microsoft.Data.SqlClient.DisableTNIRByDefaultInConnectionString";
+ #endif
- #if NET
- private static Tristate s_globalizationInvariantMode;
+ // this field is accessed through reflection in tests and should not be renamed or have the type changed without refactoring NullRow related tests
+ internal static bool? s_legacyRowVersionNullBehavior;
+ internal static bool? s_suppressInsecureTlsWarning;
+ internal static bool? s_makeReadAsyncBlocking;
+ internal static bool? s_useMinimumLoginTimeout;
+ // this field is accessed through reflection in Microsoft.Data.SqlClient.Tests.SqlParameterTests and should not be renamed or have the type changed without refactoring related tests
+ internal static bool? s_legacyVarTimeZeroScaleBehaviour;
+ internal static bool? s_useCompatibilityProcessSni;
+ internal static bool? s_useCompatibilityAsyncBehaviour;
+ internal static bool? s_useConnectionPoolV2;
+ internal static bool? s_truncateScaledDecimal;
+ internal static bool? s_ignoreServerProvidedFailoverPartner;
+ internal static bool? s_enableUserAgent;
+ internal static bool? s_multiSubnetFailoverByDefault;
- #if _WINDOWS
- private static Tristate s_useManagedNetworking;
- #endif
- #else
- private static Tristate s_disableTnirByDefault;
- #endif
+ #if NET
+ internal static bool? s_globalizationInvariantMode;
+ #endif
+ #if NET && _WINDOWS
+ internal static bool? s_useManagedNetworking;
+ #endif
+ #if NETFRAMEWORK
+ internal static bool? s_disableTnirByDefault;
+ #endif
- #if NET
- static LocalAppContextSwitches()
- {
- IAppContextSwitchOverridesSection appContextSwitch = AppConfigManager.FetchConfigurationSection(AppContextSwitchOverridesSection.Name);
+ #if NET
+ static LocalAppContextSwitches()
+ {
+ IAppContextSwitchOverridesSection appContextSwitch = AppConfigManager.FetchConfigurationSection(AppContextSwitchOverridesSection.Name);
- try
- {
- SqlAppContextSwitchManager.ApplyContextSwitches(appContextSwitch);
- }
- catch (Exception e)
- {
- // @TODO: Adopt netcore style of trace logs
- // Don't throw an exception for an invalid config file
- SqlClientEventSource.Log.TryTraceEvent(": {1}", nameof(LocalAppContextSwitches), e);
- }
+ try
+ {
+ SqlAppContextSwitchManager.ApplyContextSwitches(appContextSwitch);
}
- #endif
+ catch (Exception e)
+ {
+ // @TODO: Adopt netcore style of trace logs
+ // Don't throw an exception for an invalid config file
+ SqlClientEventSource.Log.TryTraceEvent(": {1}", nameof(LocalAppContextSwitches), e);
+ }
+ }
+ #endif
- // @TODO: Sort by name
+ // @TODO: Sort by name
- ///
- /// In TdsParser, the ProcessSni function changed significantly when the packet
- /// multiplexing code needed for high speed multi-packet column values was added.
- /// When this switch is set to true (the default), the old ProcessSni design is used.
- /// When this switch is set to false, the new experimental ProcessSni behavior using
- /// the packet multiplexer is enabled.
- ///
- public static bool UseCompatibilityProcessSni
+ ///
+ /// In TdsParser, the ProcessSni function changed significantly when the packet
+ /// multiplexing code needed for high speed multi-packet column values was added.
+ /// When this switch is set to true (the default), the old ProcessSni design is used.
+ /// When this switch is set to false, the new experimental ProcessSni behavior using
+ /// the packet multiplexer is enabled.
+ ///
+ public static bool UseCompatibilityProcessSni
+ {
+ get
{
- get
+ if (!s_useCompatibilityProcessSni.HasValue)
+
{
- if (s_useCompatibilityProcessSni == Tristate.NotInitialized)
+ // Check if the switch has been set by the AppContext switch directly
+ // If it has not been set, we default to true.
+ if (!AppContext.TryGetSwitch(UseCompatibilityProcessSniString, out bool returnedValue) || returnedValue)
{
- // Check if the switch has been set by the AppContext switch directly
- // If it has not been set, we default to true.
- if (!AppContext.TryGetSwitch(UseCompatibilityProcessSniString, out bool returnedValue) || returnedValue)
- {
- s_useCompatibilityProcessSni = Tristate.True;
- }
- else
- {
- s_useCompatibilityProcessSni = Tristate.False;
- }
+ s_useCompatibilityProcessSni = true;
+ }
+ else
+ {
+ s_useCompatibilityProcessSni = false;
}
- return s_useCompatibilityProcessSni == Tristate.True;
}
+ return s_useCompatibilityProcessSni.Value;
}
+ }
- ///
- /// In TdsParser, the async multi-packet column value fetch behavior can use a continue snapshot state
- /// for improved efficiency. When this switch is enabled (the default), the driver preserves the legacy
- /// compatibility behavior, which does not use the continue snapshot state. When disabled, the new behavior
- /// using the continue snapshot state is enabled. This switch will always return true if
- /// is enabled, because the continue state is not stable without
- /// the multiplexer.
- ///
- public static bool UseCompatibilityAsyncBehaviour
+ ///
+ /// In TdsParser, the async multi-packet column value fetch behavior can use a continue snapshot state
+ /// for improved efficiency. When this switch is enabled (the default), the driver preserves the legacy
+ /// compatibility behavior, which does not use the continue snapshot state. When disabled, the new behavior
+ /// using the continue snapshot state is enabled. This switch will always return true if
+ /// is enabled, because the continue state is not stable without
+ /// the multiplexer.
+ ///
+ public static bool UseCompatibilityAsyncBehaviour
+ {
+ get
{
- get
+ if (UseCompatibilityProcessSni)
{
- if (UseCompatibilityProcessSni)
+ // If ProcessSni compatibility mode has been enabled then the packet
+ // multiplexer has been disabled. The new async behaviour using continue
+ // point capture is only stable if the multiplexer is enabled so we must
+ // return true to enable compatibility async behaviour using only restarts.
+ return true;
+ }
+
+ if (!s_useCompatibilityAsyncBehaviour.HasValue)
+ {
+ if (!AppContext.TryGetSwitch(UseCompatibilityAsyncBehaviourString, out bool returnedValue) || returnedValue)
{
- // If ProcessSni compatibility mode has been enabled then the packet
- // multiplexer has been disabled. The new async behaviour using continue
- // point capture is only stable if the multiplexer is enabled so we must
- // return true to enable compatibility async behaviour using only restarts.
- return true;
+ s_useCompatibilityAsyncBehaviour = true;
}
-
- if (s_useCompatibilityAsyncBehaviour == Tristate.NotInitialized)
+ else
{
- if (!AppContext.TryGetSwitch(UseCompatibilityAsyncBehaviourString, out bool returnedValue) || returnedValue)
- {
- s_useCompatibilityAsyncBehaviour = Tristate.True;
- }
- else
- {
- s_useCompatibilityAsyncBehaviour = Tristate.False;
- }
+ s_useCompatibilityAsyncBehaviour = false;
}
- return s_useCompatibilityAsyncBehaviour == Tristate.True;
}
+ return s_useCompatibilityAsyncBehaviour.Value;
}
+ }
- ///
- /// When using Encrypt=false in the connection string, a security warning is output to the console if the TLS version is 1.2 or lower.
- /// This warning can be suppressed by enabling this AppContext switch.
- /// This app context switch defaults to 'false'.
- ///
- public static bool SuppressInsecureTlsWarning
+ ///
+ /// When using Encrypt=false in the connection string, a security warning is output to the console if the TLS version is 1.2 or lower.
+ /// This warning can be suppressed by enabling this AppContext switch.
+ /// This app context switch defaults to 'false'.
+ ///
+ public static bool SuppressInsecureTlsWarning
+ {
+ get
{
- get
+ if (!s_suppressInsecureTlsWarning.HasValue)
{
- if (s_suppressInsecureTlsWarning == Tristate.NotInitialized)
+ if (AppContext.TryGetSwitch(SuppressInsecureTlsWarningString, out bool returnedValue) && returnedValue)
{
- if (AppContext.TryGetSwitch(SuppressInsecureTlsWarningString, out bool returnedValue) && returnedValue)
- {
- s_suppressInsecureTlsWarning = Tristate.True;
- }
- else
- {
- s_suppressInsecureTlsWarning = Tristate.False;
- }
+ s_suppressInsecureTlsWarning = true;
+ }
+ else
+ {
+ s_suppressInsecureTlsWarning = false;
}
- return s_suppressInsecureTlsWarning == Tristate.True;
}
+ return s_suppressInsecureTlsWarning.Value;
}
+ }
- ///
- /// In System.Data.SqlClient and Microsoft.Data.SqlClient prior to 3.0.0 a field with type Timestamp/RowVersion
- /// would return an empty byte array. This switch controls whether to preserve that behaviour on newer versions
- /// of Microsoft.Data.SqlClient, if this switch returns false an appropriate null value will be returned.
- /// This app context switch defaults to 'false'.
- ///
- public static bool LegacyRowVersionNullBehavior
+ ///
+ /// In System.Data.SqlClient and Microsoft.Data.SqlClient prior to 3.0.0 a field with type Timestamp/RowVersion
+ /// would return an empty byte array. This switch controls whether to preserve that behaviour on newer versions
+ /// of Microsoft.Data.SqlClient, if this switch returns false an appropriate null value will be returned.
+ /// This app context switch defaults to 'false'.
+ ///
+ public static bool LegacyRowVersionNullBehavior
+ {
+ get
{
- get
+ if (!s_legacyRowVersionNullBehavior.HasValue)
{
- if (s_legacyRowVersionNullBehavior == Tristate.NotInitialized)
+ if (AppContext.TryGetSwitch(LegacyRowVersionNullString, out bool returnedValue) && returnedValue)
{
- if (AppContext.TryGetSwitch(LegacyRowVersionNullString, out bool returnedValue) && returnedValue)
- {
- s_legacyRowVersionNullBehavior = Tristate.True;
- }
- else
- {
- s_legacyRowVersionNullBehavior = Tristate.False;
- }
+ s_legacyRowVersionNullBehavior = true;
+ }
+ else
+ {
+ s_legacyRowVersionNullBehavior = false;
}
- return s_legacyRowVersionNullBehavior == Tristate.True;
}
+ return s_legacyRowVersionNullBehavior.Value;
}
+ }
- ///
- /// When enabled, ReadAsync runs asynchronously and does not block the calling thread.
- /// This app context switch defaults to 'false'.
- ///
- public static bool MakeReadAsyncBlocking
+ ///
+ /// When enabled, ReadAsync runs asynchronously and does not block the calling thread.
+ /// This app context switch defaults to 'false'.
+ ///
+ public static bool MakeReadAsyncBlocking
+ {
+ get
{
- get
+ if (!s_makeReadAsyncBlocking.HasValue)
{
- if (s_makeReadAsyncBlocking == Tristate.NotInitialized)
+ if (AppContext.TryGetSwitch(MakeReadAsyncBlockingString, out bool returnedValue) && returnedValue)
{
- if (AppContext.TryGetSwitch(MakeReadAsyncBlockingString, out bool returnedValue) && returnedValue)
- {
- s_makeReadAsyncBlocking = Tristate.True;
- }
- else
- {
- s_makeReadAsyncBlocking = Tristate.False;
- }
+ s_makeReadAsyncBlocking = true;
+ }
+ else
+ {
+ s_makeReadAsyncBlocking = false;
}
- return s_makeReadAsyncBlocking == Tristate.True;
}
+ return s_makeReadAsyncBlocking.Value;
}
+ }
- ///
- /// Specifies minimum login timeout to be set to 1 second instead of 0 seconds,
- /// to prevent a login attempt from waiting indefinitely.
- /// This app context switch defaults to 'true'.
- ///
- public static bool UseMinimumLoginTimeout
+ ///
+ /// Specifies minimum login timeout to be set to 1 second instead of 0 seconds,
+ /// to prevent a login attempt from waiting indefinitely.
+ /// This app context switch defaults to 'true'.
+ ///
+ public static bool UseMinimumLoginTimeout
+ {
+ get
{
- get
+ if (!s_useMinimumLoginTimeout.HasValue)
{
- if (s_useMinimumLoginTimeout == Tristate.NotInitialized)
+ if (!AppContext.TryGetSwitch(UseMinimumLoginTimeoutString, out bool returnedValue) || returnedValue)
{
- if (!AppContext.TryGetSwitch(UseMinimumLoginTimeoutString, out bool returnedValue) || returnedValue)
- {
- s_useMinimumLoginTimeout = Tristate.True;
- }
- else
- {
- s_useMinimumLoginTimeout = Tristate.False;
- }
+ s_useMinimumLoginTimeout = true;
+ }
+ else
+ {
+ s_useMinimumLoginTimeout = false;
}
- return s_useMinimumLoginTimeout == Tristate.True;
}
+ return s_useMinimumLoginTimeout.Value;
}
+ }
- ///
- /// When set to 'true' this will output a scale value of 7 (DEFAULT_VARTIME_SCALE) when the scale
- /// is explicitly set to zero for VarTime data types ('datetime2', 'datetimeoffset' and 'time')
- /// If no scale is set explicitly it will continue to output scale of 7 (DEFAULT_VARTIME_SCALE)
- /// regardless of switch value.
- /// This app context switch defaults to 'true'.
- ///
- public static bool LegacyVarTimeZeroScaleBehaviour
+ ///
+ /// When set to 'true' this will output a scale value of 7 (DEFAULT_VARTIME_SCALE) when the scale
+ /// is explicitly set to zero for VarTime data types ('datetime2', 'datetimeoffset' and 'time')
+ /// If no scale is set explicitly it will continue to output scale of 7 (DEFAULT_VARTIME_SCALE)
+ /// regardless of switch value.
+ /// This app context switch defaults to 'true'.
+ ///
+ public static bool LegacyVarTimeZeroScaleBehaviour
+ {
+ get
{
- get
+ if (!s_legacyVarTimeZeroScaleBehaviour.HasValue)
{
- if (s_legacyVarTimeZeroScaleBehaviour == Tristate.NotInitialized)
+ if (!AppContext.TryGetSwitch(LegacyVarTimeZeroScaleBehaviourString, out bool returnedValue))
{
- if (!AppContext.TryGetSwitch(LegacyVarTimeZeroScaleBehaviourString, out bool returnedValue))
- {
- s_legacyVarTimeZeroScaleBehaviour = Tristate.True;
- }
- else
- {
- s_legacyVarTimeZeroScaleBehaviour = returnedValue ? Tristate.True : Tristate.False;
- }
+ s_legacyVarTimeZeroScaleBehaviour = true;
+ }
+ else
+ {
+ s_legacyVarTimeZeroScaleBehaviour = returnedValue ? true : false;
}
- return s_legacyVarTimeZeroScaleBehaviour == Tristate.True;
}
+ return s_legacyVarTimeZeroScaleBehaviour.Value;
}
+ }
- ///
- /// When set to true, the connection pool will use the new V2 connection pool implementation.
- /// When set to false, the connection pool will use the legacy V1 implementation.
- /// This app context switch defaults to 'false'.
- ///
- public static bool UseConnectionPoolV2
+ ///
+ /// When set to true, the connection pool will use the new V2 connection pool implementation.
+ /// When set to false, the connection pool will use the legacy V1 implementation.
+ /// This app context switch defaults to 'false'.
+ ///
+ public static bool UseConnectionPoolV2
+ {
+ get
{
- get
+ if (!s_useConnectionPoolV2.HasValue)
{
- if (s_useConnectionPoolV2 == Tristate.NotInitialized)
+ if (AppContext.TryGetSwitch(UseConnectionPoolV2String, out bool returnedValue) && returnedValue)
{
- if (AppContext.TryGetSwitch(UseConnectionPoolV2String, out bool returnedValue) && returnedValue)
- {
- s_useConnectionPoolV2 = Tristate.True;
- }
- else
- {
- s_useConnectionPoolV2 = Tristate.False;
- }
+ s_useConnectionPoolV2 = true;
+ }
+ else
+ {
+ s_useConnectionPoolV2 = false;
}
- return s_useConnectionPoolV2 == Tristate.True;
}
+ return s_useConnectionPoolV2.Value;
}
+ }
- ///
- /// When set to true, TdsParser will truncate (rather than round) decimal and SqlDecimal values when scaling them.
- ///
- public static bool TruncateScaledDecimal
+ ///
+ /// When set to true, TdsParser will truncate (rather than round) decimal and SqlDecimal values when scaling them.
+ ///
+ public static bool TruncateScaledDecimal
+ {
+ get
{
- get
+ if (!s_truncateScaledDecimal.HasValue)
{
- if (s_truncateScaledDecimal == Tristate.NotInitialized)
+ if (AppContext.TryGetSwitch(TruncateScaledDecimalString, out bool returnedValue) && returnedValue)
{
- if (AppContext.TryGetSwitch(TruncateScaledDecimalString, out bool returnedValue) && returnedValue)
- {
- s_truncateScaledDecimal = Tristate.True;
- }
- else
- {
- s_truncateScaledDecimal = Tristate.False;
- }
+ s_truncateScaledDecimal = true;
+ }
+ else
+ {
+ s_truncateScaledDecimal = false;
}
- return s_truncateScaledDecimal == Tristate.True;
}
+ return s_truncateScaledDecimal.Value;
}
+ }
- ///
- /// When set to true, the failover partner provided by the server during connection
- /// will be ignored. This is useful in scenarios where the application wants to
- /// control the failover behavior explicitly (e.g. using a custom port). The application
- /// must be kept up to date with the failover configuration of the server.
- /// The application will not automatically discover a newly configured failover partner.
- ///
- /// This app context switch defaults to 'false'.
- ///
- public static bool IgnoreServerProvidedFailoverPartner
+ ///
+ /// When set to true, the failover partner provided by the server during connection
+ /// will be ignored. This is useful in scenarios where the application wants to
+ /// control the failover behavior explicitly (e.g. using a custom port). The application
+ /// must be kept up to date with the failover configuration of the server.
+ /// The application will not automatically discover a newly configured failover partner.
+ ///
+ /// This app context switch defaults to 'false'.
+ ///
+ public static bool IgnoreServerProvidedFailoverPartner
+ {
+ get
{
- get
+ if (!s_ignoreServerProvidedFailoverPartner.HasValue)
{
- if (s_ignoreServerProvidedFailoverPartner == Tristate.NotInitialized)
+ if (AppContext.TryGetSwitch(IgnoreServerProvidedFailoverPartnerString, out bool returnedValue) && returnedValue)
{
- if (AppContext.TryGetSwitch(IgnoreServerProvidedFailoverPartnerString, out bool returnedValue) && returnedValue)
- {
- s_ignoreServerProvidedFailoverPartner = Tristate.True;
- }
- else
- {
- s_ignoreServerProvidedFailoverPartner = Tristate.False;
- }
+ s_ignoreServerProvidedFailoverPartner = true;
+ }
+ else
+ {
+ s_ignoreServerProvidedFailoverPartner = false;
}
- return s_ignoreServerProvidedFailoverPartner == Tristate.True;
}
+ return s_ignoreServerProvidedFailoverPartner.Value;
}
- ///
- /// When set to true, the user agent feature is enabled and the driver will send the user agent string to the server.
- ///
- public static bool EnableUserAgent
+ }
+ ///
+ /// When set to true, the user agent feature is enabled and the driver will send the user agent string to the server.
+ ///
+ public static bool EnableUserAgent
+ {
+ get
{
- get
+ if (!s_enableUserAgent.HasValue)
{
- if (s_enableUserAgent == Tristate.NotInitialized)
+ if (AppContext.TryGetSwitch(EnableUserAgentString, out bool returnedValue) && returnedValue)
{
- if (AppContext.TryGetSwitch(EnableUserAgentString, out bool returnedValue) && returnedValue)
- {
- s_enableUserAgent = Tristate.True;
- }
- else
- {
- s_enableUserAgent = Tristate.False;
- }
+ s_enableUserAgent = true;
+ }
+ else
+ {
+ s_enableUserAgent = false;
}
- return s_enableUserAgent == Tristate.True;
}
+ return s_enableUserAgent.Value;
}
+ }
- #if NET
- ///
- /// .NET Core 2.0 and up supports Globalization Invariant mode, which reduces the size of the required libraries for
- /// applications which don't need globalization support. SqlClient requires those libraries for core functionality,
- /// and will throw exceptions later if they are not present. This switch allows SqlClient to detect this mode early.
- ///
- public static bool GlobalizationInvariantMode
+ #if NET
+ ///
+ /// .NET Core 2.0 and up supports Globalization Invariant mode, which reduces the size of the required libraries for
+ /// applications which don't need globalization support. SqlClient requires those libraries for core functionality,
+ /// and will throw exceptions later if they are not present. This switch allows SqlClient to detect this mode early.
+ ///
+ public static bool GlobalizationInvariantMode
+ {
+ get
{
- get
+ if (!s_globalizationInvariantMode.HasValue)
{
- if (s_globalizationInvariantMode == Tristate.NotInitialized)
+ // Check if invariant mode has been set by the AppContext switch directly
+ if (AppContext.TryGetSwitch(GlobalizationInvariantModeString, out bool returnedValue) && returnedValue)
{
- // Check if invariant mode has been set by the AppContext switch directly
- if (AppContext.TryGetSwitch(GlobalizationInvariantModeString, out bool returnedValue) && returnedValue)
+ s_globalizationInvariantMode = true;
+ }
+ else
+ {
+ // If the switch is not set, we check the environment variable as the first fallback
+ string? envValue = Environment.GetEnvironmentVariable(GlobalizationInvariantModeEnvironmentVariable);
+
+ if (string.Equals(envValue, bool.TrueString, StringComparison.OrdinalIgnoreCase) || string.Equals(envValue, "1", StringComparison.OrdinalIgnoreCase))
{
- s_globalizationInvariantMode = Tristate.True;
+ s_globalizationInvariantMode = true;
}
else
{
- // If the switch is not set, we check the environment variable as the first fallback
- string envValue = Environment.GetEnvironmentVariable(GlobalizationInvariantModeEnvironmentVariable);
-
- if (string.Equals(envValue, bool.TrueString, StringComparison.OrdinalIgnoreCase) || string.Equals(envValue, "1", StringComparison.OrdinalIgnoreCase))
+ // If this hasn't been manually set, it could still apply if the OS doesn't have ICU libraries installed,
+ // or if the application is a native binary with ICU support trimmed away.
+ // .NET 3.1 to 5.0 do not throw in attempting to create en-US in invariant mode, but .NET 6+ does. In
+ // such cases, catch and infer invariant mode from the exception.
+ try
{
- s_globalizationInvariantMode = Tristate.True;
+ s_globalizationInvariantMode = System.Globalization.CultureInfo.GetCultureInfo("en-US").EnglishName.Contains("Invariant")
+ ? true
+ : false;
}
- else
+ catch (System.Globalization.CultureNotFoundException)
{
- // If this hasn't been manually set, it could still apply if the OS doesn't have ICU libraries installed,
- // or if the application is a native binary with ICU support trimmed away.
- // .NET 3.1 to 5.0 do not throw in attempting to create en-US in invariant mode, but .NET 6+ does. In
- // such cases, catch and infer invariant mode from the exception.
- try
- {
- s_globalizationInvariantMode = System.Globalization.CultureInfo.GetCultureInfo("en-US").EnglishName.Contains("Invariant")
- ? Tristate.True
- : Tristate.False;
- }
- catch (System.Globalization.CultureNotFoundException)
- {
- // If the culture is not found, it means we are in invariant mode
- s_globalizationInvariantMode = Tristate.True;
- }
+ // If the culture is not found, it means we are in invariant mode
+ s_globalizationInvariantMode = true;
}
}
}
- return s_globalizationInvariantMode == Tristate.True;
}
+ return s_globalizationInvariantMode.Value;
}
- #else
- ///
- /// .NET Framework does not support Globalization Invariant mode, so this will always be false.
- ///
- public static bool GlobalizationInvariantMode
- {
- get => false;
- }
- #endif
+ }
+ #else
+ ///
+ /// .NET Framework does not support Globalization Invariant mode, so this will always be false.
+ ///
+ public static bool GlobalizationInvariantMode
+ {
+ get => false;
+ }
+ #endif
- #if NET
+ #if NET
- #if _WINDOWS
- ///
- /// When set to true, .NET Core will use the managed SNI implementation instead of the native SNI implementation.
- ///
- ///
- ///
- /// Non-Windows platforms will always use the managed networking implementation. Windows platforms will use the native SNI
- /// implementation by default, but this can be overridden by setting the AppContext switch.
- ///
- ///
- /// ILLink.Substitutions.xml allows the unused SNI implementation to be trimmed away when the corresponding AppContext
- /// switch is set at compile time. In such cases, this property will return a constant value, even if the AppContext switch is
- /// set or reset at runtime. See the ILLink.Substitutions.Windows.xml and ILLink.Substitutions.Unix.xml resource files for details.
- ///
- ///
- public static bool UseManagedNetworking
+ #if _WINDOWS
+ ///
+ /// When set to true, .NET Core will use the managed SNI implementation instead of the native SNI implementation.
+ ///
+ ///
+ ///
+ /// Non-Windows platforms will always use the managed networking implementation. Windows platforms will use the native SNI
+ /// implementation by default, but this can be overridden by setting the AppContext switch.
+ ///
+ ///
+ /// ILLink.Substitutions.xml allows the unused SNI implementation to be trimmed away when the corresponding AppContext
+ /// switch is set at compile time. In such cases, this property will return a constant value, even if the AppContext switch is
+ /// set or reset at runtime. See the ILLink.Substitutions.Windows.xml and ILLink.Substitutions.Unix.xml resource files for details.
+ ///
+ ///
+ public static bool UseManagedNetworking
+ {
+ get
{
- get
+ if (!s_useManagedNetworking.HasValue)
{
- if (s_useManagedNetworking == Tristate.NotInitialized)
+ if (!OperatingSystem.IsWindows())
{
- if (!OperatingSystem.IsWindows())
- {
- s_useManagedNetworking = Tristate.True;
- }
- else if (AppContext.TryGetSwitch(UseManagedNetworkingOnWindowsString, out bool returnedValue) && returnedValue)
- {
- s_useManagedNetworking = Tristate.True;
- }
- else
- {
- s_useManagedNetworking = Tristate.False;
- }
+ s_useManagedNetworking = true;
+ }
+ else if (AppContext.TryGetSwitch(UseManagedNetworkingOnWindowsString, out bool returnedValue) && returnedValue)
+ {
+ s_useManagedNetworking = true;
+ }
+ else
+ {
+ s_useManagedNetworking = false;
}
- return s_useManagedNetworking == Tristate.True;
}
+ return s_useManagedNetworking.Value;
}
- #else
- ///
- /// .NET Core on Unix does not support the native SNI, so this will always be true.
- ///
- public static bool UseManagedNetworking => true;
- #endif
+ }
+ #else
+ ///
+ /// .NET Core on Unix does not support the native SNI, so this will always be true.
+ ///
+ public static bool UseManagedNetworking => true;
+ #endif
- #else
- ///
- /// .NET Framework does not support the managed SNI, so this will always be false.
- ///
- public static bool UseManagedNetworking => false;
- #endif
+ #else
+ ///
+ /// .NET Framework does not support the managed SNI, so this will always be false.
+ ///
+ public static bool UseManagedNetworking => false;
+ #endif
- #if NETFRAMEWORK
- ///
- /// Transparent Network IP Resolution (TNIR) is a revision of the existing MultiSubnetFailover feature.
- /// TNIR affects the connection sequence of the driver in the case where the first resolved IP of the hostname
- /// doesn't respond and there are multiple IPs associated with the hostname.
- ///
- /// TNIR interacts with MultiSubnetFailover to provide the following three connection sequences:
- /// 0: One IP is attempted, followed by all IPs in parallel
- /// 1: All IPs are attempted in parallel
- /// 2: All IPs are attempted one after another
- ///
- /// TransparentNetworkIPResolution is enabled by default. MultiSubnetFailover is disabled by default.
- /// To disable TNIR, you can enable the app context switch.
- ///
- /// This app context switch defaults to 'false'.
- ///
- public static bool DisableTnirByDefault
+ #if NETFRAMEWORK
+ ///
+ /// Transparent Network IP Resolution (TNIR) is a revision of the existing MultiSubnetFailover feature.
+ /// TNIR affects the connection sequence of the driver in the case where the first resolved IP of the hostname
+ /// doesn't respond and there are multiple IPs associated with the hostname.
+ ///
+ /// TNIR interacts with MultiSubnetFailover to provide the following three connection sequences:
+ /// 0: One IP is attempted, followed by all IPs in parallel
+ /// 1: All IPs are attempted in parallel
+ /// 2: All IPs are attempted one after another
+ ///
+ /// TransparentNetworkIPResolution is enabled by default. MultiSubnetFailover is disabled by default.
+ /// To disable TNIR, you can enable the app context switch.
+ ///
+ /// This app context switch defaults to 'false'.
+ ///
+ public static bool DisableTnirByDefault
+ {
+ get
{
- get
+ if (!s_disableTnirByDefault.HasValue)
{
- if (s_disableTnirByDefault == Tristate.NotInitialized)
+ if (AppContext.TryGetSwitch(DisableTnirByDefaultString, out bool returnedValue) && returnedValue)
{
- if (AppContext.TryGetSwitch(DisableTnirByDefaultString, out bool returnedValue) && returnedValue)
- {
- s_disableTnirByDefault = Tristate.True;
- }
- else
- {
- s_disableTnirByDefault = Tristate.False;
- }
+ s_disableTnirByDefault = true;
+ }
+ else
+ {
+ s_disableTnirByDefault = false;
}
- return s_disableTnirByDefault == Tristate.True;
}
+ return s_disableTnirByDefault.Value;
}
+ }
#endif
- ///
- /// When set to true, the default value for MultiSubnetFailover connection string property
- /// will be true instead of false. This enables parallel IP connection attempts for
- /// improved connection times in multi-subnet environments.
- /// This app context switch defaults to 'false'.
- ///
- public static bool EnableMultiSubnetFailoverByDefault
+ ///
+ /// When set to true, the default value for MultiSubnetFailover connection string property
+ /// will be true instead of false. This enables parallel IP connection attempts for
+ /// improved connection times in multi-subnet environments.
+ /// This app context switch defaults to 'false'.
+ ///
+ public static bool EnableMultiSubnetFailoverByDefault
+ {
+ get
{
- get
+ if (!s_multiSubnetFailoverByDefault.HasValue)
{
- if (s_multiSubnetFailoverByDefault == Tristate.NotInitialized)
+ if (AppContext.TryGetSwitch(EnableMultiSubnetFailoverByDefaultString, out bool returnedValue) && returnedValue)
{
- if (AppContext.TryGetSwitch(EnableMultiSubnetFailoverByDefaultString, out bool returnedValue) && returnedValue)
- {
- s_multiSubnetFailoverByDefault = Tristate.True;
- }
- else
- {
- s_multiSubnetFailoverByDefault = Tristate.False;
- }
+ s_multiSubnetFailoverByDefault = true;
+ }
+ else
+ {
+ s_multiSubnetFailoverByDefault = false;
}
- return s_multiSubnetFailoverByDefault == Tristate.True;
}
+ return s_multiSubnetFailoverByDefault.Value;
}
}
}
diff --git a/src/Microsoft.Data.SqlClient/tests/Common/Common.csproj b/src/Microsoft.Data.SqlClient/tests/Common/Common.csproj
index ffcd2869f9..ec728d6a98 100644
--- a/src/Microsoft.Data.SqlClient/tests/Common/Common.csproj
+++ b/src/Microsoft.Data.SqlClient/tests/Common/Common.csproj
@@ -8,6 +8,7 @@
$(ObjFolder)$(Configuration).$(Platform).$(AssemblyName)
$(BinFolder)$(Configuration).$(Platform).$(AssemblyName)
true
+ enable
diff --git a/src/Microsoft.Data.SqlClient/tests/Common/Fixtures/CertificateFixtureBase.cs b/src/Microsoft.Data.SqlClient/tests/Common/Fixtures/CertificateFixtureBase.cs
index f97aedfe4e..e3dd3902f0 100644
--- a/src/Microsoft.Data.SqlClient/tests/Common/Fixtures/CertificateFixtureBase.cs
+++ b/src/Microsoft.Data.SqlClient/tests/Common/Fixtures/CertificateFixtureBase.cs
@@ -67,7 +67,7 @@ protected X509Certificate2 CreateCertificate(string subjectName, IEnumerable csc.Location == storeLocation && csc.Name == storeName);
+ CertificateStoreContext? storeContext = _certificateStoreModifications.Find(csc => csc.Location == storeLocation && csc.Name == storeName);
if (storeContext == null)
{
diff --git a/src/Microsoft.Data.SqlClient/tests/Common/Fixtures/ColumnEncryptionCertificateFixture.cs b/src/Microsoft.Data.SqlClient/tests/Common/Fixtures/ColumnEncryptionCertificateFixture.cs
index a4aa84842f..05b54ed091 100644
--- a/src/Microsoft.Data.SqlClient/tests/Common/Fixtures/ColumnEncryptionCertificateFixture.cs
+++ b/src/Microsoft.Data.SqlClient/tests/Common/Fixtures/ColumnEncryptionCertificateFixture.cs
@@ -25,7 +25,7 @@ public sealed class ColumnEncryptionCertificateFixture : CertificateFixtureBase
public X509Certificate2 CertificateWithoutPrivateKey { get; }
private readonly X509Certificate2 _currentUserCertificate;
- private readonly X509Certificate2 _localMachineCertificate;
+ private readonly X509Certificate2? _localMachineCertificate;
public ColumnEncryptionCertificateFixture()
{
@@ -57,11 +57,19 @@ public ColumnEncryptionCertificateFixture()
public X509Certificate2 GetCertificate(StoreLocation storeLocation)
{
- return storeLocation == StoreLocation.CurrentUser
- ? _currentUserCertificate
- : storeLocation == StoreLocation.LocalMachine && IsAdmin
- ? _localMachineCertificate
- : throw new InvalidOperationException("Attempted to retrieve the certificate added to the local machine store; this requires administrator rights.");
+ if (storeLocation == StoreLocation.CurrentUser)
+ {
+ return _currentUserCertificate;
+ }
+
+ if (storeLocation == StoreLocation.LocalMachine &&
+ IsAdmin &&
+ _localMachineCertificate is not null)
+ {
+ return _localMachineCertificate!;
+ }
+
+ throw new InvalidOperationException("Attempted to retrieve the certificate added to the local machine store; this requires administrator rights.");
}
public static bool IsAdmin
diff --git a/src/Microsoft.Data.SqlClient/tests/Common/Fixtures/ColumnMasterKeyCertificateFixture.cs b/src/Microsoft.Data.SqlClient/tests/Common/Fixtures/ColumnMasterKeyCertificateFixture.cs
index a91ca7a0e0..7b431782af 100644
--- a/src/Microsoft.Data.SqlClient/tests/Common/Fixtures/ColumnMasterKeyCertificateFixture.cs
+++ b/src/Microsoft.Data.SqlClient/tests/Common/Fixtures/ColumnMasterKeyCertificateFixture.cs
@@ -21,7 +21,7 @@ public ColumnMasterKeyCertificateFixture()
{
}
- public X509Certificate2 ColumnMasterKeyCertificate { get; }
+ public X509Certificate2? ColumnMasterKeyCertificate { get; }
protected ColumnMasterKeyCertificateFixture(bool createCertificate)
{
diff --git a/src/Microsoft.Data.SqlClient/tests/Common/Fixtures/CspCertificateFixture.cs b/src/Microsoft.Data.SqlClient/tests/Common/Fixtures/CspCertificateFixture.cs
index 74c4ca0325..56463e91b8 100644
--- a/src/Microsoft.Data.SqlClient/tests/Common/Fixtures/CspCertificateFixture.cs
+++ b/src/Microsoft.Data.SqlClient/tests/Common/Fixtures/CspCertificateFixture.cs
@@ -32,17 +32,17 @@ public CspCertificateFixture()
public string CspCertificatePath { get; }
- public string CspKeyPath { get; }
+ public string? CspKeyPath { get; }
- private string GetCspPathFromCertificate()
+ private string? GetCspPathFromCertificate()
{
- RSA privateKey = CspCertificate.GetRSAPrivateKey();
+ RSA? privateKey = CspCertificate.GetRSAPrivateKey();
if (privateKey is RSACryptoServiceProvider csp)
{
return string.Concat(csp.CspKeyContainerInfo.ProviderName, @"/", csp.CspKeyContainerInfo.KeyContainerName);
}
- else if (privateKey is RSACng cng)
+ else if (privateKey is RSACng cng && cng.Key.Provider is not null)
{
return string.Concat(cng.Key.Provider.Provider, @"/", cng.Key.KeyName);
}
diff --git a/src/Microsoft.Data.SqlClient/tests/Common/LocalAppContextSwitchesHelper.cs b/src/Microsoft.Data.SqlClient/tests/Common/LocalAppContextSwitchesHelper.cs
index 7d90df85fe..a04f4ebcf5 100644
--- a/src/Microsoft.Data.SqlClient/tests/Common/LocalAppContextSwitchesHelper.cs
+++ b/src/Microsoft.Data.SqlClient/tests/Common/LocalAppContextSwitchesHelper.cs
@@ -1,6 +1,5 @@
using System;
-using System.Collections.Generic;
-using System.Reflection;
+using System.Threading;
namespace Microsoft.Data.SqlClient.Tests.Common;
@@ -15,388 +14,118 @@ namespace Microsoft.Data.SqlClient.Tests.Common;
///
/// https://en.wikipedia.org/wiki/Resource_acquisition_is_initialization
///
-/// This class is not thread-aware and should not be used concurrently.
+/// Only one instance can exist at a time. Overlapping constructor calls will
+/// wait until the previous instance is disposed.
///
public sealed class LocalAppContextSwitchesHelper : IDisposable
{
#region Private Fields
- // These fields are used to expose LocalAppContextSwitches's properties.
- private readonly PropertyInfo _legacyRowVersionNullBehaviorProperty;
- private readonly PropertyInfo _suppressInsecureTlsWarningProperty;
- private readonly PropertyInfo _makeReadAsyncBlockingProperty;
- private readonly PropertyInfo _useMinimumLoginTimeoutProperty;
- private readonly PropertyInfo _legacyVarTimeZeroScaleBehaviourProperty;
- private readonly PropertyInfo _useCompatibilityProcessSniProperty;
- private readonly PropertyInfo _useCompatibilityAsyncBehaviourProperty;
- private readonly PropertyInfo _useConnectionPoolV2Property;
- private readonly PropertyInfo _truncateScaledDecimalProperty;
- private readonly PropertyInfo _ignoreServerProvidedFailoverPartner;
- private readonly PropertyInfo _enableUserAgent;
- private readonly PropertyInfo _enableMultiSubnetFailoverByDefaultProperty;
-#if NET
- private readonly PropertyInfo _globalizationInvariantModeProperty;
- #endif
-
- #if NET && _WINDOWS
- private readonly PropertyInfo _useManagedNetworkingProperty;
- #endif
-
- #if NETFRAMEWORK
- private readonly PropertyInfo _disableTnirByDefaultProperty;
- #endif
+ // This semaphore ensures that only one instance of this class may exist at
+ // a time.
+ private static readonly SemaphoreSlim s_instanceLock = new(1, 1);
// These fields are used to capture the original switch values.
- private readonly FieldInfo _legacyRowVersionNullBehaviorField;
- private readonly Tristate _legacyRowVersionNullBehaviorOriginal;
- private readonly FieldInfo _suppressInsecureTlsWarningField;
- private readonly Tristate _suppressInsecureTlsWarningOriginal;
- private readonly FieldInfo _makeReadAsyncBlockingField;
- private readonly Tristate _makeReadAsyncBlockingOriginal;
- private readonly FieldInfo _useMinimumLoginTimeoutField;
- private readonly Tristate _useMinimumLoginTimeoutOriginal;
- private readonly FieldInfo _legacyVarTimeZeroScaleBehaviourField;
- private readonly Tristate _legacyVarTimeZeroScaleBehaviourOriginal;
- private readonly FieldInfo _useCompatibilityProcessSniField;
- private readonly Tristate _useCompatibilityProcessSniOriginal;
- private readonly FieldInfo _useCompatibilityAsyncBehaviourField;
- private readonly Tristate _useCompatibilityAsyncBehaviourOriginal;
- private readonly FieldInfo _useConnectionPoolV2Field;
- private readonly Tristate _useConnectionPoolV2Original;
- private readonly FieldInfo _truncateScaledDecimalField;
- private readonly Tristate _truncateScaledDecimalOriginal;
- private readonly FieldInfo _ignoreServerProvidedFailoverPartnerField;
- private readonly Tristate _ignoreServerProvidedFailoverPartnerOriginal;
- private readonly FieldInfo _enableUserAgentField;
- private readonly Tristate _enableUserAgentOriginal;
- private readonly FieldInfo _multiSubnetFailoverByDefaultField;
- private readonly Tristate _multiSubnetFailoverByDefaultOriginal;
-#if NET
- private readonly FieldInfo _globalizationInvariantModeField;
- private readonly Tristate _globalizationInvariantModeOriginal;
+ private readonly bool? _legacyRowVersionNullBehaviorOriginal;
+ private readonly bool? _suppressInsecureTlsWarningOriginal;
+ private readonly bool? _makeReadAsyncBlockingOriginal;
+ private readonly bool? _useMinimumLoginTimeoutOriginal;
+ private readonly bool? _legacyVarTimeZeroScaleBehaviourOriginal;
+ private readonly bool? _useCompatibilityProcessSniOriginal;
+ private readonly bool? _useCompatibilityAsyncBehaviourOriginal;
+ private readonly bool? _useConnectionPoolV2Original;
+ private readonly bool? _truncateScaledDecimalOriginal;
+ private readonly bool? _ignoreServerProvidedFailoverPartnerOriginal;
+ private readonly bool? _enableUserAgentOriginal;
+ private readonly bool? _multiSubnetFailoverByDefaultOriginal;
+
+ #if NET
+ private readonly bool? _globalizationInvariantModeOriginal;
#endif
#if NET && _WINDOWS
- private readonly FieldInfo _useManagedNetworkingField;
- private readonly Tristate _useManagedNetworkingOriginal;
+ private readonly bool? _useManagedNetworkingOriginal;
#endif
#if NETFRAMEWORK
- private readonly FieldInfo _disableTnirByDefaultField;
- private readonly Tristate _disableTnirByDefaultOriginal;
+ private readonly bool? _disableTnirByDefaultOriginal;
#endif
#endregion
- #region Public Types
-
- ///
- /// This enum is used to represent the state of a switch.
- ///
- /// It is a copy of the Tristate enum from LocalAppContextSwitches.
- ///
- public enum Tristate : byte
- {
- NotInitialized = 0,
- False = 1,
- True = 2
- }
-
- #endregion
-
#region Construction
///
/// Construct to capture all existing switch values.
+ ///
+ /// This call will block, waiting for any previous instance to be disposed
+ /// before completing construction.
///
- ///
- ///
- /// Throws if any values cannot be captured.
- ///
public LocalAppContextSwitchesHelper()
{
- // Acquire a handle to the LocalAppContextSwitches type.
- var assembly = typeof(SqlCommandBuilder).Assembly;
- var switchesType = assembly.GetType(
- "Microsoft.Data.SqlClient.LocalAppContextSwitches");
- if (switchesType == null)
+ // Wait for any previous instance to be disposed.
+ //
+ // We are only willing to wait a short time to avoid deadlocks.
+ //
+ if (! s_instanceLock.Wait(TimeSpan.FromSeconds(5)))
{
- throw new Exception("Unable to find LocalAppContextSwitches type.");
+ throw new InvalidOperationException(
+ "Timeout waiting for previous LocalAppContextSwitchesHelper " +
+ "instance to be disposed.");
}
- // A local helper to acquire a handle to a property.
- void InitProperty(string name, out PropertyInfo property)
- {
- var prop = switchesType.GetProperty(
- name, BindingFlags.Public | BindingFlags.Static);
- if (prop == null)
- {
- throw new Exception($"Unable to find {name} property.");
- }
- property = prop;
- }
-
- // Acquire handles to all of the public properties of
- // LocalAppContextSwitches.
- InitProperty(
- "LegacyRowVersionNullBehavior",
- out _legacyRowVersionNullBehaviorProperty);
-
- InitProperty(
- "SuppressInsecureTlsWarning",
- out _suppressInsecureTlsWarningProperty);
-
- InitProperty(
- "MakeReadAsyncBlocking",
- out _makeReadAsyncBlockingProperty);
-
- InitProperty(
- "UseMinimumLoginTimeout",
- out _useMinimumLoginTimeoutProperty);
-
- InitProperty(
- "LegacyVarTimeZeroScaleBehaviour",
- out _legacyVarTimeZeroScaleBehaviourProperty);
-
- InitProperty(
- "UseCompatibilityProcessSni",
- out _useCompatibilityProcessSniProperty);
-
- InitProperty(
- "UseCompatibilityAsyncBehaviour",
- out _useCompatibilityAsyncBehaviourProperty);
-
- InitProperty(
- "UseConnectionPoolV2",
- out _useConnectionPoolV2Property);
-
- InitProperty(
- "TruncateScaledDecimal",
- out _truncateScaledDecimalProperty);
-
- InitProperty(
- "IgnoreServerProvidedFailoverPartner",
- out _ignoreServerProvidedFailoverPartner);
-
- InitProperty(
- "EnableUserAgent",
- out _enableUserAgent);
-
- InitProperty(
- "EnableMultiSubnetFailoverByDefault",
- out _enableMultiSubnetFailoverByDefaultProperty);
-
-#if NET
- InitProperty(
- "GlobalizationInvariantMode",
- out _globalizationInvariantModeProperty);
+ _legacyRowVersionNullBehaviorOriginal = LocalAppContextSwitches.s_legacyRowVersionNullBehavior;
+ _suppressInsecureTlsWarningOriginal = LocalAppContextSwitches.s_suppressInsecureTlsWarning;
+ _makeReadAsyncBlockingOriginal = LocalAppContextSwitches.s_makeReadAsyncBlocking;
+ _useMinimumLoginTimeoutOriginal = LocalAppContextSwitches.s_useMinimumLoginTimeout;
+ _legacyVarTimeZeroScaleBehaviourOriginal = LocalAppContextSwitches.s_legacyVarTimeZeroScaleBehaviour;
+ _useCompatibilityProcessSniOriginal = LocalAppContextSwitches.s_useCompatibilityProcessSni;
+ _useCompatibilityAsyncBehaviourOriginal = LocalAppContextSwitches.s_useCompatibilityAsyncBehaviour;
+ _useConnectionPoolV2Original = LocalAppContextSwitches.s_useConnectionPoolV2;
+ _truncateScaledDecimalOriginal = LocalAppContextSwitches.s_truncateScaledDecimal;
+ _ignoreServerProvidedFailoverPartnerOriginal = LocalAppContextSwitches.s_ignoreServerProvidedFailoverPartner;
+ _enableUserAgentOriginal = LocalAppContextSwitches.s_enableUserAgent;
+ _multiSubnetFailoverByDefaultOriginal = LocalAppContextSwitches.s_multiSubnetFailoverByDefault;
+ #if NET
+ _globalizationInvariantModeOriginal = LocalAppContextSwitches.s_globalizationInvariantMode;
#endif
-
#if NET && _WINDOWS
- InitProperty(
- "UseManagedNetworking",
- out _useManagedNetworkingProperty);
- #endif
-
- #if NETFRAMEWORK
- InitProperty(
- "DisableTnirByDefault",
- out _disableTnirByDefaultProperty);
- #endif
-
- // A local helper to capture the original value of a switch.
- void InitField(string name, out FieldInfo field, out Tristate value)
- {
- var fieldInfo =
- switchesType.GetField(
- name, BindingFlags.NonPublic | BindingFlags.Static);
- if (fieldInfo == null)
- {
- throw new Exception($"Unable to find {name} field.");
- }
- field = fieldInfo;
- value = GetValue(field);
- }
-
- // Capture the original value of each switch.
- InitField(
- "s_legacyRowVersionNullBehavior",
- out _legacyRowVersionNullBehaviorField,
- out _legacyRowVersionNullBehaviorOriginal);
-
- InitField(
- "s_suppressInsecureTlsWarning",
- out _suppressInsecureTlsWarningField,
- out _suppressInsecureTlsWarningOriginal);
-
- InitField(
- "s_makeReadAsyncBlocking",
- out _makeReadAsyncBlockingField,
- out _makeReadAsyncBlockingOriginal);
-
- InitField(
- "s_useMinimumLoginTimeout",
- out _useMinimumLoginTimeoutField,
- out _useMinimumLoginTimeoutOriginal);
-
- InitField(
- "s_legacyVarTimeZeroScaleBehaviour",
- out _legacyVarTimeZeroScaleBehaviourField,
- out _legacyVarTimeZeroScaleBehaviourOriginal);
-
- InitField(
- "s_useCompatibilityProcessSni",
- out _useCompatibilityProcessSniField,
- out _useCompatibilityProcessSniOriginal);
-
- InitField(
- "s_useCompatibilityAsyncBehaviour",
- out _useCompatibilityAsyncBehaviourField,
- out _useCompatibilityAsyncBehaviourOriginal);
-
- InitField(
- "s_useConnectionPoolV2",
- out _useConnectionPoolV2Field,
- out _useConnectionPoolV2Original);
-
- InitField(
- "s_truncateScaledDecimal",
- out _truncateScaledDecimalField,
- out _truncateScaledDecimalOriginal);
-
- InitField(
- "s_ignoreServerProvidedFailoverPartner",
- out _ignoreServerProvidedFailoverPartnerField,
- out _ignoreServerProvidedFailoverPartnerOriginal);
-
- InitField(
- "s_enableUserAgent",
- out _enableUserAgentField,
- out _enableUserAgentOriginal);
-
- InitField(
- "s_multiSubnetFailoverByDefault",
- out _multiSubnetFailoverByDefaultField,
- out _multiSubnetFailoverByDefaultOriginal);
-
-#if NET
- InitField(
- "s_globalizationInvariantMode",
- out _globalizationInvariantModeField,
- out _globalizationInvariantModeOriginal);
+ _useManagedNetworkingOriginal = LocalAppContextSwitches.s_useManagedNetworking;
#endif
-
- #if NET && _WINDOWS
- InitField(
- "s_useManagedNetworking",
- out _useManagedNetworkingField,
- out _useManagedNetworkingOriginal);
-#endif
-
#if NETFRAMEWORK
- InitField(
- "s_disableTnirByDefault",
- out _disableTnirByDefaultField,
- out _disableTnirByDefaultOriginal);
+ _disableTnirByDefaultOriginal = LocalAppContextSwitches.s_disableTnirByDefault;
#endif
}
///
- /// Disposal restores all original switch values as a best effort.
+ /// Disposal restores all original switch values and releases the instance lock.
///
- ///
- ///
- /// Throws if any values could not be restored after trying to restore all
- /// values.
- ///
public void Dispose()
{
- List failedFields = new();
-
- void RestoreField(FieldInfo field, Tristate value)
- {
- try
- {
- SetValue(field, value);
- }
- catch (Exception)
- {
- failedFields.Add(field.Name);
- }
- }
-
- RestoreField(
- _legacyRowVersionNullBehaviorField,
- _legacyRowVersionNullBehaviorOriginal);
-
- RestoreField(
- _suppressInsecureTlsWarningField,
- _suppressInsecureTlsWarningOriginal);
-
- RestoreField(
- _makeReadAsyncBlockingField,
- _makeReadAsyncBlockingOriginal);
-
- RestoreField(
- _useMinimumLoginTimeoutField,
- _useMinimumLoginTimeoutOriginal);
-
- RestoreField(
- _legacyVarTimeZeroScaleBehaviourField,
- _legacyVarTimeZeroScaleBehaviourOriginal);
-
- RestoreField(
- _useCompatibilityProcessSniField,
- _useCompatibilityProcessSniOriginal);
-
- RestoreField(
- _useCompatibilityAsyncBehaviourField,
- _useCompatibilityAsyncBehaviourOriginal);
-
- RestoreField(
- _useConnectionPoolV2Field,
- _useConnectionPoolV2Original);
-
- RestoreField(
- _truncateScaledDecimalField,
- _truncateScaledDecimalOriginal);
-
- RestoreField(
- _ignoreServerProvidedFailoverPartnerField,
- _ignoreServerProvidedFailoverPartnerOriginal);
-
- RestoreField(
- _enableUserAgentField,
- _enableUserAgentOriginal);
-
- RestoreField(
- _multiSubnetFailoverByDefaultField,
- _multiSubnetFailoverByDefaultOriginal);
-
+ LocalAppContextSwitches.s_legacyRowVersionNullBehavior = _legacyRowVersionNullBehaviorOriginal;
+ LocalAppContextSwitches.s_suppressInsecureTlsWarning = _suppressInsecureTlsWarningOriginal;
+ LocalAppContextSwitches.s_makeReadAsyncBlocking = _makeReadAsyncBlockingOriginal;
+ LocalAppContextSwitches.s_useMinimumLoginTimeout = _useMinimumLoginTimeoutOriginal;
+ LocalAppContextSwitches.s_legacyVarTimeZeroScaleBehaviour = _legacyVarTimeZeroScaleBehaviourOriginal;
+ LocalAppContextSwitches.s_useCompatibilityProcessSni = _useCompatibilityProcessSniOriginal;
+ LocalAppContextSwitches.s_useCompatibilityAsyncBehaviour = _useCompatibilityAsyncBehaviourOriginal;
+ LocalAppContextSwitches.s_useConnectionPoolV2 = _useConnectionPoolV2Original;
+ LocalAppContextSwitches.s_truncateScaledDecimal = _truncateScaledDecimalOriginal;
+ LocalAppContextSwitches.s_ignoreServerProvidedFailoverPartner = _ignoreServerProvidedFailoverPartnerOriginal;
+ LocalAppContextSwitches.s_enableUserAgent = _enableUserAgentOriginal;
+ LocalAppContextSwitches.s_multiSubnetFailoverByDefault = _multiSubnetFailoverByDefaultOriginal;
#if NET
- RestoreField(
- _globalizationInvariantModeField,
- _globalizationInvariantModeOriginal);
+ LocalAppContextSwitches.s_globalizationInvariantMode = _globalizationInvariantModeOriginal;
#endif
-
#if NET && _WINDOWS
- RestoreField(
- _useManagedNetworkingField,
- _useManagedNetworkingOriginal);
+ LocalAppContextSwitches.s_useManagedNetworking = _useManagedNetworkingOriginal;
#endif
-
#if NETFRAMEWORK
- RestoreField(
- _disableTnirByDefaultField,
- _disableTnirByDefaultOriginal);
+ LocalAppContextSwitches.s_disableTnirByDefault = _disableTnirByDefaultOriginal;
#endif
- if (failedFields.Count > 0)
- {
- throw new Exception(
- "Failed to restore the following fields: " +
- string.Join(", ", failedFields));
- }
+ // Release the lock to allow another instance to be created.
+ s_instanceLock.Release();
}
#endregion
@@ -409,7 +138,7 @@ void RestoreField(FieldInfo field, Tristate value)
///
public bool LegacyRowVersionNullBehavior
{
- get => (bool)_legacyRowVersionNullBehaviorProperty.GetValue(null);
+ get => LocalAppContextSwitches.LegacyRowVersionNullBehavior;
}
///
@@ -417,7 +146,7 @@ public bool LegacyRowVersionNullBehavior
///
public bool SuppressInsecureTlsWarning
{
- get => (bool)_suppressInsecureTlsWarningProperty.GetValue(null);
+ get => LocalAppContextSwitches.SuppressInsecureTlsWarning;
}
///
@@ -425,7 +154,7 @@ public bool SuppressInsecureTlsWarning
///
public bool MakeReadAsyncBlocking
{
- get => (bool)_makeReadAsyncBlockingProperty.GetValue(null);
+ get => LocalAppContextSwitches.MakeReadAsyncBlocking;
}
///
@@ -433,7 +162,7 @@ public bool MakeReadAsyncBlocking
///
public bool UseMinimumLoginTimeout
{
- get => (bool)_useMinimumLoginTimeoutProperty.GetValue(null);
+ get => LocalAppContextSwitches.UseMinimumLoginTimeout;
}
///
@@ -442,7 +171,7 @@ public bool UseMinimumLoginTimeout
///
public bool LegacyVarTimeZeroScaleBehaviour
{
- get => (bool)_legacyVarTimeZeroScaleBehaviourProperty.GetValue(null);
+ get => LocalAppContextSwitches.LegacyVarTimeZeroScaleBehaviour;
}
///
@@ -450,7 +179,7 @@ public bool LegacyVarTimeZeroScaleBehaviour
///
public bool UseCompatibilityProcessSni
{
- get => (bool)_useCompatibilityProcessSniProperty.GetValue(null);
+ get => LocalAppContextSwitches.UseCompatibilityProcessSni;
}
///
@@ -459,7 +188,7 @@ public bool UseCompatibilityProcessSni
///
public bool UseCompatibilityAsyncBehaviour
{
- get => (bool)_useCompatibilityAsyncBehaviourProperty.GetValue(null);
+ get => LocalAppContextSwitches.UseCompatibilityAsyncBehaviour;
}
///
@@ -467,7 +196,7 @@ public bool UseCompatibilityAsyncBehaviour
///
public bool UseConnectionPoolV2
{
- get => (bool)_useConnectionPoolV2Property.GetValue(null);
+ get => LocalAppContextSwitches.UseConnectionPoolV2;
}
///
@@ -475,22 +204,22 @@ public bool UseConnectionPoolV2
///
public bool TruncateScaledDecimal
{
- get => (bool)_truncateScaledDecimalProperty.GetValue(null);
+ get => LocalAppContextSwitches.TruncateScaledDecimal;
}
public bool IgnoreServerProvidedFailoverPartner
{
- get => (bool)_ignoreServerProvidedFailoverPartner.GetValue(null);
+ get => LocalAppContextSwitches.IgnoreServerProvidedFailoverPartner;
}
public bool EnableUserAgent
{
- get => (bool)_enableUserAgent.GetValue(null);
+ get => LocalAppContextSwitches.EnableUserAgent;
}
public bool EnableMultiSubnetFailoverByDefault
{
- get => (bool)_enableMultiSubnetFailoverByDefaultProperty.GetValue(null);
+ get => LocalAppContextSwitches.EnableMultiSubnetFailoverByDefault;
}
#if NET
@@ -499,7 +228,7 @@ public bool EnableMultiSubnetFailoverByDefault
///
public bool GlobalizationInvariantMode
{
- get => (bool)_globalizationInvariantModeProperty.GetValue(null);
+ get => LocalAppContextSwitches.GlobalizationInvariantMode;
}
#endif
@@ -509,7 +238,7 @@ public bool GlobalizationInvariantMode
///
public bool UseManagedNetworking
{
- get => (bool)_useManagedNetworkingProperty.GetValue(null);
+ get => LocalAppContextSwitches.UseManagedNetworking;
}
#endif
@@ -519,7 +248,7 @@ public bool UseManagedNetworking
///
public bool DisableTnirByDefault
{
- get => (bool)_disableTnirByDefaultProperty.GetValue(null);
+ get => LocalAppContextSwitches.DisableTnirByDefault;
}
#endif
@@ -531,116 +260,116 @@ public bool DisableTnirByDefault
/// Get or set the LocalAppContextSwitches.LegacyRowVersionNullBehavior
/// switch value.
///
- public Tristate LegacyRowVersionNullBehaviorField
+ public bool? LegacyRowVersionNullBehaviorValue
{
- get => GetValue(_legacyRowVersionNullBehaviorField);
- set => SetValue(_legacyRowVersionNullBehaviorField, value);
+ get => LocalAppContextSwitches.s_legacyRowVersionNullBehavior;
+ set => LocalAppContextSwitches.s_legacyRowVersionNullBehavior = value;
}
///
/// Get or set the LocalAppContextSwitches.SuppressInsecureTlsWarning
/// switch value.
///
- public Tristate SuppressInsecureTlsWarningField
+ public bool? SuppressInsecureTlsWarningValue
{
- get => GetValue(_suppressInsecureTlsWarningField);
- set => SetValue(_suppressInsecureTlsWarningField, value);
+ get => LocalAppContextSwitches.s_suppressInsecureTlsWarning;
+ set => LocalAppContextSwitches.s_suppressInsecureTlsWarning = value;
}
///
/// Get or set the LocalAppContextSwitches.MakeReadAsyncBlocking switch
/// value.
///
- public Tristate MakeReadAsyncBlockingField
+ public bool? MakeReadAsyncBlockingValue
{
- get => GetValue(_makeReadAsyncBlockingField);
- set => SetValue(_makeReadAsyncBlockingField, value);
+ get => LocalAppContextSwitches.s_makeReadAsyncBlocking;
+ set => LocalAppContextSwitches.s_makeReadAsyncBlocking = value;
}
///
/// Get or set the LocalAppContextSwitches.UseMinimumLoginTimeout switch
/// value.
///
- public Tristate UseMinimumLoginTimeoutField
+ public bool? UseMinimumLoginTimeoutValue
{
- get => GetValue(_useMinimumLoginTimeoutField);
- set => SetValue(_useMinimumLoginTimeoutField, value);
+ get => LocalAppContextSwitches.s_useMinimumLoginTimeout;
+ set => LocalAppContextSwitches.s_useMinimumLoginTimeout = value;
}
///
/// Get or set the LocalAppContextSwitches.LegacyVarTimeZeroScaleBehaviour
/// switch value.
///
- public Tristate LegacyVarTimeZeroScaleBehaviourField
+ public bool? LegacyVarTimeZeroScaleBehaviourValue
{
- get => GetValue(_legacyVarTimeZeroScaleBehaviourField);
- set => SetValue(_legacyVarTimeZeroScaleBehaviourField, value);
+ get => LocalAppContextSwitches.s_legacyVarTimeZeroScaleBehaviour;
+ set => LocalAppContextSwitches.s_legacyVarTimeZeroScaleBehaviour = value;
}
///
/// Get or set the LocalAppContextSwitches.UseCompatibilityProcessSni switch
/// value.
///
- public Tristate UseCompatibilityProcessSniField
+ public bool? UseCompatibilityProcessSniValue
{
- get => GetValue(_useCompatibilityProcessSniField);
- set => SetValue(_useCompatibilityProcessSniField, value);
+ get => LocalAppContextSwitches.s_useCompatibilityProcessSni;
+ set => LocalAppContextSwitches.s_useCompatibilityProcessSni = value;
}
///
/// Get or set the LocalAppContextSwitches.UseCompatibilityAsyncBehaviour
/// switch value.
///
- public Tristate UseCompatibilityAsyncBehaviourField
+ public bool? UseCompatibilityAsyncBehaviourValue
{
- get => GetValue(_useCompatibilityAsyncBehaviourField);
- set => SetValue(_useCompatibilityAsyncBehaviourField, value);
+ get => LocalAppContextSwitches.s_useCompatibilityAsyncBehaviour;
+ set => LocalAppContextSwitches.s_useCompatibilityAsyncBehaviour = value;
}
///
/// Get or set the LocalAppContextSwitches.UseConnectionPoolV2 switch value.
///
- public Tristate UseConnectionPoolV2Field
+ public bool? UseConnectionPoolV2Value
{
- get => GetValue(_useConnectionPoolV2Field);
- set => SetValue(_useConnectionPoolV2Field, value);
+ get => LocalAppContextSwitches.s_useConnectionPoolV2;
+ set => LocalAppContextSwitches.s_useConnectionPoolV2 = value;
}
///
/// Get or set the LocalAppContextSwitches.TruncateScaledDecimal switch value.
///
- public Tristate TruncateScaledDecimalField
+ public bool? TruncateScaledDecimalValue
{
- get => GetValue(_truncateScaledDecimalField);
- set => SetValue(_truncateScaledDecimalField, value);
+ get => LocalAppContextSwitches.s_truncateScaledDecimal;
+ set => LocalAppContextSwitches.s_truncateScaledDecimal = value;
}
- public Tristate IgnoreServerProvidedFailoverPartnerField
+ public bool? IgnoreServerProvidedFailoverPartnerValue
{
- get => GetValue(_ignoreServerProvidedFailoverPartnerField);
- set => SetValue(_ignoreServerProvidedFailoverPartnerField, value);
+ get => LocalAppContextSwitches.s_ignoreServerProvidedFailoverPartner;
+ set => LocalAppContextSwitches.s_ignoreServerProvidedFailoverPartner = value;
}
- public Tristate EnableUserAgentField
+ public bool? EnableUserAgentValue
{
- get => GetValue(_enableUserAgentField);
- set => SetValue(_enableUserAgentField, value);
+ get => LocalAppContextSwitches.s_enableUserAgent;
+ set => LocalAppContextSwitches.s_enableUserAgent = value;
}
- public Tristate EnableMultiSubnetFailoverByDefaultField
+ public bool? EnableMultiSubnetFailoverByDefaultValue
{
- get => GetValue(_multiSubnetFailoverByDefaultField);
- set => SetValue(_multiSubnetFailoverByDefaultField, value);
+ get => LocalAppContextSwitches.s_multiSubnetFailoverByDefault;
+ set => LocalAppContextSwitches.s_multiSubnetFailoverByDefault = value;
}
#if NET
///
/// Get or set the LocalAppContextSwitches.GlobalizationInvariantMode switch value.
///
- public Tristate GlobalizationInvariantModeField
+ public bool? GlobalizationInvariantModeValue
{
- get => GetValue(_globalizationInvariantModeField);
- set => SetValue(_globalizationInvariantModeField, value);
+ get => LocalAppContextSwitches.s_globalizationInvariantMode;
+ set => LocalAppContextSwitches.s_globalizationInvariantMode = value;
}
#endif
@@ -648,10 +377,10 @@ public Tristate GlobalizationInvariantModeField
///
/// Get or set the LocalAppContextSwitches.UseManagedNetworking switch value.
///
- public Tristate UseManagedNetworkingField
+ public bool? UseManagedNetworkingValue
{
- get => GetValue(_useManagedNetworkingField);
- set => SetValue(_useManagedNetworkingField, value);
+ get => LocalAppContextSwitches.s_useManagedNetworking;
+ set => LocalAppContextSwitches.s_useManagedNetworking = value;
}
#endif
@@ -660,34 +389,12 @@ public Tristate UseManagedNetworkingField
/// Get or set the LocalAppContextSwitches.DisableTnirByDefault switch
/// value.
///
- public Tristate DisableTnirByDefaultField
+ public bool? DisableTnirByDefaultValue
{
- get => GetValue(_disableTnirByDefaultField);
- set => SetValue(_disableTnirByDefaultField, value);
+ get => LocalAppContextSwitches.s_disableTnirByDefault;
+ set => LocalAppContextSwitches.s_disableTnirByDefault = value;
}
#endif
#endregion
-
- #region Private Helpers
-
- // Get the value of the given field, or throw if it is null.
- private static Tristate GetValue(FieldInfo field)
- {
- var value = field.GetValue(null);
- if (value is null)
- {
- throw new Exception($"Field {field.Name} has a null value.");
- }
-
- return (Tristate)value;
- }
-
- // Set the value of the given field.
- private static void SetValue(FieldInfo field, Tristate value)
- {
- field.SetValue(null, (byte)value);
- }
-
- #endregion
}
diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlParameterTest.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlParameterTest.cs
index f62c5a70c8..8506e35e98 100644
--- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlParameterTest.cs
+++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlParameterTest.cs
@@ -1952,10 +1952,8 @@ public void SqlDateTime2Scale_Legacy(int? setScale, byte outputScale, bool legac
lock (_parameterLegacyScaleLock)
{
using SwitchesHelper switches = new SwitchesHelper();
- switches.LegacyVarTimeZeroScaleBehaviourField =
- legacyVarTimeZeroScaleSwitchValue
- ? SwitchesHelper.Tristate.True
- : SwitchesHelper.Tristate.False;
+ switches.LegacyVarTimeZeroScaleBehaviourValue =
+ legacyVarTimeZeroScaleSwitchValue;
var parameter = new SqlParameter
{
diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataReaderTest/DataReaderTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataReaderTest/DataReaderTest.cs
index b071eeaf5a..4b7ac38cfa 100644
--- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataReaderTest/DataReaderTest.cs
+++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataReaderTest/DataReaderTest.cs
@@ -268,7 +268,7 @@ public static void CheckNullRowVersionIsDBNull()
lock (s_rowVersionLock)
{
using SwitchesHelper helper = new();
- helper.LegacyRowVersionNullBehaviorField = SwitchesHelper.Tristate.False;
+ helper.LegacyRowVersionNullBehaviorValue = false;
using SqlConnection con = new(DataTestUtility.TCPConnectionString);
con.Open();
@@ -869,7 +869,7 @@ public static void CheckLegacyNullRowVersionIsEmptyArray()
lock (s_rowVersionLock)
{
using SwitchesHelper helper = new();
- helper.LegacyRowVersionNullBehaviorField = SwitchesHelper.Tristate.True;
+ helper.LegacyRowVersionNullBehaviorValue = true;
using SqlConnection con = new(DataTestUtility.TCPConnectionString);
con.Open();
diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/ParametersTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/ParametersTest.cs
index 7ae4a82fb9..b16368a9c0 100644
--- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/ParametersTest.cs
+++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/ParametersTest.cs
@@ -589,7 +589,7 @@ public static void TestScaledDecimalParameter_CommandInsert(string connectionStr
{
using (SqlCommand cmd = connection.CreateCommand())
{
- appContextSwitchesHelper.TruncateScaledDecimalField = truncateScaledDecimal ? LocalAppContextSwitchesHelper.Tristate.True : LocalAppContextSwitchesHelper.Tristate.False;
+ appContextSwitchesHelper.TruncateScaledDecimalValue = truncateScaledDecimal;
var p = new SqlParameter("@Value", null)
{
@@ -636,7 +636,7 @@ public static void TestScaledDecimalParameter_BulkCopy(string connectionString,
}
bulkCopy.DestinationTableName = tableName;
- appContextSwitchesHelper.TruncateScaledDecimalField = truncateScaledDecimal ? LocalAppContextSwitchesHelper.Tristate.True : LocalAppContextSwitchesHelper.Tristate.False;
+ appContextSwitchesHelper.TruncateScaledDecimalValue = truncateScaledDecimal;
bulkCopy.WriteToServer(table);
}
Assert.True(ValidateInsertedValues(connection, tableName, truncateScaledDecimal), $"Invalid test happened with connection string [{connection.ConnectionString}]");
@@ -681,7 +681,7 @@ public static void TestScaledDecimalTVP_CommandSP(string connectionString, bool
table.Rows.Add(newRow);
}
p.Value = table;
- appContextSwitchesHelper.TruncateScaledDecimalField = truncateScaledDecimal ? LocalAppContextSwitchesHelper.Tristate.True : LocalAppContextSwitchesHelper.Tristate.False;
+ appContextSwitchesHelper.TruncateScaledDecimalValue = truncateScaledDecimal;
cmd.ExecuteNonQuery();
}
// TVP always rounds data without attention to the configuration.
diff --git a/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlConnectionStringTest.cs b/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlConnectionStringTest.cs
index e413821932..6133671881 100644
--- a/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlConnectionStringTest.cs
+++ b/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlConnectionStringTest.cs
@@ -16,25 +16,25 @@ public SqlConnectionStringTest()
#if NETFRAMEWORK
[Theory]
- [InlineData("test.database.windows.net", true, Tristate.True, true)]
- [InlineData("test.database.windows.net", false, Tristate.True, false)]
- [InlineData("test.database.windows.net", null, Tristate.True, false)]
- [InlineData("test.database.windows.net", true, Tristate.False, true)]
- [InlineData("test.database.windows.net", false, Tristate.False, false)]
- [InlineData("test.database.windows.net", null, Tristate.False, true)]
- [InlineData("test.database.windows.net", true, Tristate.NotInitialized, true)]
- [InlineData("test.database.windows.net", false, Tristate.NotInitialized, false)]
- [InlineData("test.database.windows.net", null, Tristate.NotInitialized, true)]
- [InlineData("my.test.server", true, Tristate.True, true)]
- [InlineData("my.test.server", false, Tristate.True, false)]
- [InlineData("my.test.server", null, Tristate.True, false)]
- [InlineData("my.test.server", true, Tristate.False, true)]
- [InlineData("my.test.server", false, Tristate.False, false)]
- [InlineData("my.test.server", null, Tristate.False, true)]
- [InlineData("my.test.server", true, Tristate.NotInitialized, true)]
- [InlineData("my.test.server", false, Tristate.NotInitialized, false)]
- [InlineData("my.test.server", null, Tristate.NotInitialized, true)]
- public void TestDefaultTnir(string dataSource, bool? tnirEnabledInConnString, Tristate tnirDisabledAppContext, bool expectedValue)
+ [InlineData("test.database.windows.net", true, true, true)]
+ [InlineData("test.database.windows.net", false, true, false)]
+ [InlineData("test.database.windows.net", null, true, false)]
+ [InlineData("test.database.windows.net", true, false, true)]
+ [InlineData("test.database.windows.net", false, false, false)]
+ [InlineData("test.database.windows.net", null, false, true)]
+ [InlineData("test.database.windows.net", true, null, true)]
+ [InlineData("test.database.windows.net", false, null, false)]
+ [InlineData("test.database.windows.net", null, null, true)]
+ [InlineData("my.test.server", true, true, true)]
+ [InlineData("my.test.server", false, true, false)]
+ [InlineData("my.test.server", null, true, false)]
+ [InlineData("my.test.server", true, false, true)]
+ [InlineData("my.test.server", false, false, false)]
+ [InlineData("my.test.server", null, false, true)]
+ [InlineData("my.test.server", true, null, true)]
+ [InlineData("my.test.server", false, null, false)]
+ [InlineData("my.test.server", null, null, true)]
+ public void TestDefaultTnir(string dataSource, bool? tnirEnabledInConnString, bool? tnirDisabledAppContext, bool expectedValue)
{
// Note: TNIR is only supported on .NET Framework.
// Note: TNIR is disabled by default for Azure SQL Database servers (i.e. *.database.windows.net)
@@ -43,7 +43,7 @@ public void TestDefaultTnir(string dataSource, bool? tnirEnabledInConnString, Tr
// the value of TransparentNetworkIPResolution property in SqlConnectionString.
// Arrange
- _appContextSwitchHelper.DisableTnirByDefaultField = tnirDisabledAppContext;
+ _appContextSwitchHelper.DisableTnirByDefaultValue = tnirDisabledAppContext;
// Act
SqlConnectionStringBuilder builder = new();
@@ -62,16 +62,16 @@ public void TestDefaultTnir(string dataSource, bool? tnirEnabledInConnString, Tr
/// Test MSF values when set through connection string and through app context switch.
///
[Theory]
- [InlineData(true, Tristate.True, true)]
- [InlineData(false, Tristate.True, false)]
- [InlineData(null, Tristate.True, true)]
- [InlineData(true, Tristate.False, true)]
- [InlineData(false, Tristate.False, false)]
- [InlineData(null, Tristate.False, false)]
- [InlineData(null, Tristate.NotInitialized, false)]
- public void TestDefaultMultiSubnetFailover(bool? msfInConnString, Tristate msfEnabledAppContext, bool expectedValue)
+ [InlineData(true, true, true)]
+ [InlineData(false, true, false)]
+ [InlineData(null, true, true)]
+ [InlineData(true, false, true)]
+ [InlineData(false, false, false)]
+ [InlineData(null, false, false)]
+ [InlineData(null, null, false)]
+ public void TestDefaultMultiSubnetFailover(bool? msfInConnString, bool? msfEnabledAppContext, bool expectedValue)
{
- _appContextSwitchHelper.EnableMultiSubnetFailoverByDefaultField = msfEnabledAppContext;
+ _appContextSwitchHelper.EnableMultiSubnetFailoverByDefaultValue = msfEnabledAppContext;
SqlConnectionStringBuilder builder = new();
if (msfInConnString.HasValue)
@@ -89,7 +89,7 @@ public void TestDefaultMultiSubnetFailover(bool? msfInConnString, Tristate msfEn
[Fact]
public void TestMultiSubnetFailoverWithFailoverPartnerThrows()
{
- _appContextSwitchHelper.EnableMultiSubnetFailoverByDefaultField = Tristate.True;
+ _appContextSwitchHelper.EnableMultiSubnetFailoverByDefaultValue = true;
SqlConnectionStringBuilder builder = new()
{
diff --git a/src/Microsoft.Data.SqlClient/tests/UnitTests/SimulatedServerTests/ConnectionFailoverTests.cs b/src/Microsoft.Data.SqlClient/tests/UnitTests/SimulatedServerTests/ConnectionFailoverTests.cs
index dfc37d2720..f815a909d8 100644
--- a/src/Microsoft.Data.SqlClient/tests/UnitTests/SimulatedServerTests/ConnectionFailoverTests.cs
+++ b/src/Microsoft.Data.SqlClient/tests/UnitTests/SimulatedServerTests/ConnectionFailoverTests.cs
@@ -529,7 +529,7 @@ public void TransientFault_IgnoreServerProvidedFailoverPartner_ShouldConnectToUs
{
// Arrange
using LocalAppContextSwitchesHelper switchesHelper = new();
- switchesHelper.IgnoreServerProvidedFailoverPartnerField = LocalAppContextSwitchesHelper.Tristate.True;
+ switchesHelper.IgnoreServerProvidedFailoverPartnerValue = true;
using TdsServer failoverServer = new(
new TdsServerArguments
diff --git a/src/Microsoft.Data.SqlClient/tests/UnitTests/SimulatedServerTests/ConnectionTests.cs b/src/Microsoft.Data.SqlClient/tests/UnitTests/SimulatedServerTests/ConnectionTests.cs
index 9dad33aa1d..daae6d7598 100644
--- a/src/Microsoft.Data.SqlClient/tests/UnitTests/SimulatedServerTests/ConnectionTests.cs
+++ b/src/Microsoft.Data.SqlClient/tests/UnitTests/SimulatedServerTests/ConnectionTests.cs
@@ -839,7 +839,7 @@ public void TestConnWithUserAgentFeatureExtension(bool forceAck)
{
// Make sure needed switch is enabled
using LocalAppContextSwitchesHelper switchesHelper = new();
- switchesHelper.EnableUserAgentField = LocalAppContextSwitchesHelper.Tristate.True;
+ switchesHelper.EnableUserAgentValue = true;
using var server = new TdsServer();
server.Start();
@@ -935,7 +935,7 @@ public void TestConnWithoutUserAgentFeatureExtension()
{
// Disable the client-side UserAgent field entirely
using LocalAppContextSwitchesHelper switchesHelper = new();
- switchesHelper.EnableUserAgentField = LocalAppContextSwitchesHelper.Tristate.False;
+ switchesHelper.EnableUserAgentValue = false;
using var server = new TdsServer();
server.Start();
From ae224616d05e708c1fea71b18b38012d65c243c0 Mon Sep 17 00:00:00 2001
From: Paul Medynski <31868385+paulmedynski@users.noreply.github.com>
Date: Thu, 18 Dec 2025 11:21:07 -0400
Subject: [PATCH 2/7] - Reverted back to an atomic byte for the switch values.
---
.../Data/SqlClient/LocalAppContextSwitches.cs | 189 ++++++++++--------
.../Common/LocalAppContextSwitchesHelper.cs | 170 ++++++++++------
2 files changed, 218 insertions(+), 141 deletions(-)
diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/LocalAppContextSwitches.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/LocalAppContextSwitches.cs
index b1441db382..e01422a875 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/LocalAppContextSwitches.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/LocalAppContextSwitches.cs
@@ -10,6 +10,8 @@ namespace Microsoft.Data.SqlClient;
internal static class LocalAppContextSwitches
{
+ #region Switch Names
+
private const string MakeReadAsyncBlockingString = @"Switch.Microsoft.Data.SqlClient.MakeReadAsyncBlocking";
private const string LegacyRowVersionNullString = @"Switch.Microsoft.Data.SqlClient.LegacyRowVersionNullBehavior";
private const string SuppressInsecureTlsWarningString = @"Switch.Microsoft.Data.SqlClient.SuppressInsecureTLSWarning";
@@ -34,31 +36,50 @@ internal static class LocalAppContextSwitches
private const string DisableTnirByDefaultString = @"Switch.Microsoft.Data.SqlClient.DisableTNIRByDefaultInConnectionString";
#endif
- // this field is accessed through reflection in tests and should not be renamed or have the type changed without refactoring NullRow related tests
- internal static bool? s_legacyRowVersionNullBehavior;
- internal static bool? s_suppressInsecureTlsWarning;
- internal static bool? s_makeReadAsyncBlocking;
- internal static bool? s_useMinimumLoginTimeout;
- // this field is accessed through reflection in Microsoft.Data.SqlClient.Tests.SqlParameterTests and should not be renamed or have the type changed without refactoring related tests
- internal static bool? s_legacyVarTimeZeroScaleBehaviour;
- internal static bool? s_useCompatibilityProcessSni;
- internal static bool? s_useCompatibilityAsyncBehaviour;
- internal static bool? s_useConnectionPoolV2;
- internal static bool? s_truncateScaledDecimal;
- internal static bool? s_ignoreServerProvidedFailoverPartner;
- internal static bool? s_enableUserAgent;
- internal static bool? s_multiSubnetFailoverByDefault;
+ #endregion
+
+ #region Switch Values
+
+ // We use a byte-based enum to track the value of each switch. This plays
+ // nicely with threaded access. A nullable bool would seem to be the
+ // obvious choice, but the way nullable bools are implemented in the CLR
+ // makes them not thread-safe without using locks (the HasValue and Value
+ // properties can get out of sync if one thread is writing while another is
+ // reading).
+ internal enum SwitchValue : byte
+ {
+ None = 0,
+ True = 1,
+ False = 2
+ }
+
+ internal static SwitchValue s_legacyRowVersionNullBehavior = SwitchValue.None;
+ internal static SwitchValue s_suppressInsecureTlsWarning = SwitchValue.None;
+ internal static SwitchValue s_makeReadAsyncBlocking = SwitchValue.None;
+ internal static SwitchValue s_useMinimumLoginTimeout = SwitchValue.None;
+ internal static SwitchValue s_legacyVarTimeZeroScaleBehaviour = SwitchValue.None;
+ internal static SwitchValue s_useCompatibilityProcessSni = SwitchValue.None;
+ internal static SwitchValue s_useCompatibilityAsyncBehaviour = SwitchValue.None;
+ internal static SwitchValue s_useConnectionPoolV2 = SwitchValue.None;
+ internal static SwitchValue s_truncateScaledDecimal = SwitchValue.None;
+ internal static SwitchValue s_ignoreServerProvidedFailoverPartner = SwitchValue.None;
+ internal static SwitchValue s_enableUserAgent = SwitchValue.None;
+ internal static SwitchValue s_multiSubnetFailoverByDefault = SwitchValue.None;
#if NET
- internal static bool? s_globalizationInvariantMode;
+ internal static SwitchValue s_globalizationInvariantMode;
#endif
#if NET && _WINDOWS
- internal static bool? s_useManagedNetworking;
+ internal static TriState s_useManagedNetworking;
#endif
#if NETFRAMEWORK
- internal static bool? s_disableTnirByDefault;
+ internal static SwitchValue s_disableTnirByDefault;
#endif
+ #endregion
+
+ #region Static Initialization
+
#if NET
static LocalAppContextSwitches()
{
@@ -77,6 +98,10 @@ static LocalAppContextSwitches()
}
#endif
+ #endregion
+
+ #region Switch Properties
+
// @TODO: Sort by name
///
@@ -90,21 +115,21 @@ public static bool UseCompatibilityProcessSni
{
get
{
- if (!s_useCompatibilityProcessSni.HasValue)
+ if (s_useCompatibilityProcessSni == SwitchValue.None)
{
// Check if the switch has been set by the AppContext switch directly
// If it has not been set, we default to true.
if (!AppContext.TryGetSwitch(UseCompatibilityProcessSniString, out bool returnedValue) || returnedValue)
{
- s_useCompatibilityProcessSni = true;
+ s_useCompatibilityProcessSni = SwitchValue.True;
}
else
{
- s_useCompatibilityProcessSni = false;
+ s_useCompatibilityProcessSni = SwitchValue.False;
}
}
- return s_useCompatibilityProcessSni.Value;
+ return s_useCompatibilityProcessSni == SwitchValue.True;
}
}
@@ -129,18 +154,18 @@ public static bool UseCompatibilityAsyncBehaviour
return true;
}
- if (!s_useCompatibilityAsyncBehaviour.HasValue)
+ if (s_useCompatibilityAsyncBehaviour == SwitchValue.None)
{
if (!AppContext.TryGetSwitch(UseCompatibilityAsyncBehaviourString, out bool returnedValue) || returnedValue)
{
- s_useCompatibilityAsyncBehaviour = true;
+ s_useCompatibilityAsyncBehaviour = SwitchValue.True;
}
else
{
- s_useCompatibilityAsyncBehaviour = false;
+ s_useCompatibilityAsyncBehaviour = SwitchValue.False;
}
}
- return s_useCompatibilityAsyncBehaviour.Value;
+ return s_useCompatibilityAsyncBehaviour == SwitchValue.True;
}
}
@@ -153,18 +178,18 @@ public static bool SuppressInsecureTlsWarning
{
get
{
- if (!s_suppressInsecureTlsWarning.HasValue)
+ if (s_suppressInsecureTlsWarning == SwitchValue.None)
{
if (AppContext.TryGetSwitch(SuppressInsecureTlsWarningString, out bool returnedValue) && returnedValue)
{
- s_suppressInsecureTlsWarning = true;
+ s_suppressInsecureTlsWarning = SwitchValue.True;
}
else
{
- s_suppressInsecureTlsWarning = false;
+ s_suppressInsecureTlsWarning = SwitchValue.False;
}
}
- return s_suppressInsecureTlsWarning.Value;
+ return s_suppressInsecureTlsWarning == SwitchValue.True;
}
}
@@ -178,18 +203,18 @@ public static bool LegacyRowVersionNullBehavior
{
get
{
- if (!s_legacyRowVersionNullBehavior.HasValue)
+ if (s_legacyRowVersionNullBehavior == SwitchValue.None)
{
if (AppContext.TryGetSwitch(LegacyRowVersionNullString, out bool returnedValue) && returnedValue)
{
- s_legacyRowVersionNullBehavior = true;
+ s_legacyRowVersionNullBehavior = SwitchValue.True;
}
else
{
- s_legacyRowVersionNullBehavior = false;
+ s_legacyRowVersionNullBehavior = SwitchValue.False;
}
}
- return s_legacyRowVersionNullBehavior.Value;
+ return s_legacyRowVersionNullBehavior == SwitchValue.True;
}
}
@@ -201,18 +226,18 @@ public static bool MakeReadAsyncBlocking
{
get
{
- if (!s_makeReadAsyncBlocking.HasValue)
+ if (s_makeReadAsyncBlocking == SwitchValue.None)
{
if (AppContext.TryGetSwitch(MakeReadAsyncBlockingString, out bool returnedValue) && returnedValue)
{
- s_makeReadAsyncBlocking = true;
+ s_makeReadAsyncBlocking = SwitchValue.True;
}
else
{
- s_makeReadAsyncBlocking = false;
+ s_makeReadAsyncBlocking = SwitchValue.False;
}
}
- return s_makeReadAsyncBlocking.Value;
+ return s_makeReadAsyncBlocking == SwitchValue.True;
}
}
@@ -225,18 +250,18 @@ public static bool UseMinimumLoginTimeout
{
get
{
- if (!s_useMinimumLoginTimeout.HasValue)
+ if (s_useMinimumLoginTimeout == SwitchValue.None)
{
if (!AppContext.TryGetSwitch(UseMinimumLoginTimeoutString, out bool returnedValue) || returnedValue)
{
- s_useMinimumLoginTimeout = true;
+ s_useMinimumLoginTimeout = SwitchValue.True;
}
else
{
- s_useMinimumLoginTimeout = false;
+ s_useMinimumLoginTimeout = SwitchValue.False;
}
}
- return s_useMinimumLoginTimeout.Value;
+ return s_useMinimumLoginTimeout == SwitchValue.True;
}
}
@@ -252,18 +277,18 @@ public static bool LegacyVarTimeZeroScaleBehaviour
{
get
{
- if (!s_legacyVarTimeZeroScaleBehaviour.HasValue)
+ if (s_legacyVarTimeZeroScaleBehaviour == SwitchValue.None)
{
if (!AppContext.TryGetSwitch(LegacyVarTimeZeroScaleBehaviourString, out bool returnedValue))
{
- s_legacyVarTimeZeroScaleBehaviour = true;
+ s_legacyVarTimeZeroScaleBehaviour = SwitchValue.True;
}
else
{
- s_legacyVarTimeZeroScaleBehaviour = returnedValue ? true : false;
+ s_legacyVarTimeZeroScaleBehaviour = returnedValue ? SwitchValue.True : SwitchValue.False;
}
}
- return s_legacyVarTimeZeroScaleBehaviour.Value;
+ return s_legacyVarTimeZeroScaleBehaviour == SwitchValue.True;
}
}
@@ -276,18 +301,18 @@ public static bool UseConnectionPoolV2
{
get
{
- if (!s_useConnectionPoolV2.HasValue)
+ if (s_useConnectionPoolV2 == SwitchValue.None)
{
if (AppContext.TryGetSwitch(UseConnectionPoolV2String, out bool returnedValue) && returnedValue)
{
- s_useConnectionPoolV2 = true;
+ s_useConnectionPoolV2 = SwitchValue.True;
}
else
{
- s_useConnectionPoolV2 = false;
+ s_useConnectionPoolV2 = SwitchValue.False;
}
}
- return s_useConnectionPoolV2.Value;
+ return s_useConnectionPoolV2 == SwitchValue.True;
}
}
@@ -298,18 +323,18 @@ public static bool TruncateScaledDecimal
{
get
{
- if (!s_truncateScaledDecimal.HasValue)
+ if (s_truncateScaledDecimal == SwitchValue.None)
{
if (AppContext.TryGetSwitch(TruncateScaledDecimalString, out bool returnedValue) && returnedValue)
{
- s_truncateScaledDecimal = true;
+ s_truncateScaledDecimal = SwitchValue.True;
}
else
{
- s_truncateScaledDecimal = false;
+ s_truncateScaledDecimal = SwitchValue.False;
}
}
- return s_truncateScaledDecimal.Value;
+ return s_truncateScaledDecimal == SwitchValue.True;
}
}
@@ -326,18 +351,18 @@ public static bool IgnoreServerProvidedFailoverPartner
{
get
{
- if (!s_ignoreServerProvidedFailoverPartner.HasValue)
+ if (s_ignoreServerProvidedFailoverPartner == SwitchValue.None)
{
if (AppContext.TryGetSwitch(IgnoreServerProvidedFailoverPartnerString, out bool returnedValue) && returnedValue)
{
- s_ignoreServerProvidedFailoverPartner = true;
+ s_ignoreServerProvidedFailoverPartner = SwitchValue.True;
}
else
{
- s_ignoreServerProvidedFailoverPartner = false;
+ s_ignoreServerProvidedFailoverPartner = SwitchValue.False;
}
}
- return s_ignoreServerProvidedFailoverPartner.Value;
+ return s_ignoreServerProvidedFailoverPartner == SwitchValue.True;
}
}
///
@@ -347,18 +372,18 @@ public static bool EnableUserAgent
{
get
{
- if (!s_enableUserAgent.HasValue)
+ if (s_enableUserAgent == SwitchValue.None)
{
if (AppContext.TryGetSwitch(EnableUserAgentString, out bool returnedValue) && returnedValue)
{
- s_enableUserAgent = true;
+ s_enableUserAgent = SwitchValue.True;
}
else
{
- s_enableUserAgent = false;
+ s_enableUserAgent = SwitchValue.False;
}
}
- return s_enableUserAgent.Value;
+ return s_enableUserAgent == SwitchValue.True;
}
}
@@ -372,12 +397,12 @@ public static bool GlobalizationInvariantMode
{
get
{
- if (!s_globalizationInvariantMode.HasValue)
+ if (s_globalizationInvariantMode == SwitchValue.None)
{
// Check if invariant mode has been set by the AppContext switch directly
if (AppContext.TryGetSwitch(GlobalizationInvariantModeString, out bool returnedValue) && returnedValue)
{
- s_globalizationInvariantMode = true;
+ s_globalizationInvariantMode = SwitchValue.True;
}
else
{
@@ -386,7 +411,7 @@ public static bool GlobalizationInvariantMode
if (string.Equals(envValue, bool.TrueString, StringComparison.OrdinalIgnoreCase) || string.Equals(envValue, "1", StringComparison.OrdinalIgnoreCase))
{
- s_globalizationInvariantMode = true;
+ s_globalizationInvariantMode = SwitchValue.True;
}
else
{
@@ -397,18 +422,18 @@ public static bool GlobalizationInvariantMode
try
{
s_globalizationInvariantMode = System.Globalization.CultureInfo.GetCultureInfo("en-US").EnglishName.Contains("Invariant")
- ? true
- : false;
+ ? SwitchValue.True
+ : SwitchValue.False;
}
catch (System.Globalization.CultureNotFoundException)
{
// If the culture is not found, it means we are in invariant mode
- s_globalizationInvariantMode = true;
+ s_globalizationInvariantMode = SwitchValue.True;
}
}
}
}
- return s_globalizationInvariantMode.Value;
+ return s_globalizationInvariantMode == SwitchValue.True;
}
}
#else
@@ -442,22 +467,22 @@ public static bool UseManagedNetworking
{
get
{
- if (!s_useManagedNetworking.HasValue)
+ if (s_useManagedNetworking == TriState.None)
{
if (!OperatingSystem.IsWindows())
{
- s_useManagedNetworking = true;
+ s_useManagedNetworking = TriState.True;
}
else if (AppContext.TryGetSwitch(UseManagedNetworkingOnWindowsString, out bool returnedValue) && returnedValue)
{
- s_useManagedNetworking = true;
+ s_useManagedNetworking = TriState.True;
}
else
{
- s_useManagedNetworking = false;
+ s_useManagedNetworking = TriState.False;
}
}
- return s_useManagedNetworking.Value;
+ return s_useManagedNetworking == TriState.True;
}
}
#else
@@ -494,18 +519,18 @@ public static bool DisableTnirByDefault
{
get
{
- if (!s_disableTnirByDefault.HasValue)
+ if (s_disableTnirByDefault == SwitchValue.None)
{
if (AppContext.TryGetSwitch(DisableTnirByDefaultString, out bool returnedValue) && returnedValue)
{
- s_disableTnirByDefault = true;
+ s_disableTnirByDefault = SwitchValue.True;
}
else
{
- s_disableTnirByDefault = false;
+ s_disableTnirByDefault = SwitchValue.False;
}
}
- return s_disableTnirByDefault.Value;
+ return s_disableTnirByDefault == SwitchValue.True;
}
}
#endif
@@ -520,18 +545,20 @@ public static bool EnableMultiSubnetFailoverByDefault
{
get
{
- if (!s_multiSubnetFailoverByDefault.HasValue)
+ if (s_multiSubnetFailoverByDefault == SwitchValue.None)
{
if (AppContext.TryGetSwitch(EnableMultiSubnetFailoverByDefaultString, out bool returnedValue) && returnedValue)
{
- s_multiSubnetFailoverByDefault = true;
+ s_multiSubnetFailoverByDefault = SwitchValue.True;
}
else
{
- s_multiSubnetFailoverByDefault = false;
+ s_multiSubnetFailoverByDefault = SwitchValue.False;
}
}
- return s_multiSubnetFailoverByDefault.Value;
+ return s_multiSubnetFailoverByDefault == SwitchValue.True;
}
}
+
+ #endregion
}
diff --git a/src/Microsoft.Data.SqlClient/tests/Common/LocalAppContextSwitchesHelper.cs b/src/Microsoft.Data.SqlClient/tests/Common/LocalAppContextSwitchesHelper.cs
index a04f4ebcf5..5182e7b7e7 100644
--- a/src/Microsoft.Data.SqlClient/tests/Common/LocalAppContextSwitchesHelper.cs
+++ b/src/Microsoft.Data.SqlClient/tests/Common/LocalAppContextSwitchesHelper.cs
@@ -74,26 +74,41 @@ public LocalAppContextSwitchesHelper()
"instance to be disposed.");
}
- _legacyRowVersionNullBehaviorOriginal = LocalAppContextSwitches.s_legacyRowVersionNullBehavior;
- _suppressInsecureTlsWarningOriginal = LocalAppContextSwitches.s_suppressInsecureTlsWarning;
- _makeReadAsyncBlockingOriginal = LocalAppContextSwitches.s_makeReadAsyncBlocking;
- _useMinimumLoginTimeoutOriginal = LocalAppContextSwitches.s_useMinimumLoginTimeout;
- _legacyVarTimeZeroScaleBehaviourOriginal = LocalAppContextSwitches.s_legacyVarTimeZeroScaleBehaviour;
- _useCompatibilityProcessSniOriginal = LocalAppContextSwitches.s_useCompatibilityProcessSni;
- _useCompatibilityAsyncBehaviourOriginal = LocalAppContextSwitches.s_useCompatibilityAsyncBehaviour;
- _useConnectionPoolV2Original = LocalAppContextSwitches.s_useConnectionPoolV2;
- _truncateScaledDecimalOriginal = LocalAppContextSwitches.s_truncateScaledDecimal;
- _ignoreServerProvidedFailoverPartnerOriginal = LocalAppContextSwitches.s_ignoreServerProvidedFailoverPartner;
- _enableUserAgentOriginal = LocalAppContextSwitches.s_enableUserAgent;
- _multiSubnetFailoverByDefaultOriginal = LocalAppContextSwitches.s_multiSubnetFailoverByDefault;
+ _legacyRowVersionNullBehaviorOriginal =
+ Convert(LocalAppContextSwitches.s_legacyRowVersionNullBehavior);
+ _suppressInsecureTlsWarningOriginal =
+ Convert(LocalAppContextSwitches.s_suppressInsecureTlsWarning);
+ _makeReadAsyncBlockingOriginal =
+ Convert(LocalAppContextSwitches.s_makeReadAsyncBlocking);
+ _useMinimumLoginTimeoutOriginal =
+ Convert(LocalAppContextSwitches.s_useMinimumLoginTimeout);
+ _legacyVarTimeZeroScaleBehaviourOriginal =
+ Convert(LocalAppContextSwitches.s_legacyVarTimeZeroScaleBehaviour);
+ _useCompatibilityProcessSniOriginal =
+ Convert(LocalAppContextSwitches.s_useCompatibilityProcessSni);
+ _useCompatibilityAsyncBehaviourOriginal =
+ Convert(LocalAppContextSwitches.s_useCompatibilityAsyncBehaviour);
+ _useConnectionPoolV2Original =
+ Convert(LocalAppContextSwitches.s_useConnectionPoolV2);
+ _truncateScaledDecimalOriginal =
+ Convert(LocalAppContextSwitches.s_truncateScaledDecimal);
+ _ignoreServerProvidedFailoverPartnerOriginal =
+ Convert(LocalAppContextSwitches.s_ignoreServerProvidedFailoverPartner);
+ _enableUserAgentOriginal =
+ Convert(LocalAppContextSwitches.s_enableUserAgent);
+ _multiSubnetFailoverByDefaultOriginal =
+ Convert(LocalAppContextSwitches.s_multiSubnetFailoverByDefault);
#if NET
- _globalizationInvariantModeOriginal = LocalAppContextSwitches.s_globalizationInvariantMode;
+ _globalizationInvariantModeOriginal =
+ Convert(LocalAppContextSwitches.s_globalizationInvariantMode);
#endif
#if NET && _WINDOWS
- _useManagedNetworkingOriginal = LocalAppContextSwitches.s_useManagedNetworking;
+ _useManagedNetworkingOriginal =
+ Convert(LocalAppContextSwitches.s_useManagedNetworking);
#endif
#if NETFRAMEWORK
- _disableTnirByDefaultOriginal = LocalAppContextSwitches.s_disableTnirByDefault;
+ _disableTnirByDefaultOriginal =
+ Convert(LocalAppContextSwitches.s_disableTnirByDefault);
#endif
}
@@ -102,26 +117,41 @@ public LocalAppContextSwitchesHelper()
///
public void Dispose()
{
- LocalAppContextSwitches.s_legacyRowVersionNullBehavior = _legacyRowVersionNullBehaviorOriginal;
- LocalAppContextSwitches.s_suppressInsecureTlsWarning = _suppressInsecureTlsWarningOriginal;
- LocalAppContextSwitches.s_makeReadAsyncBlocking = _makeReadAsyncBlockingOriginal;
- LocalAppContextSwitches.s_useMinimumLoginTimeout = _useMinimumLoginTimeoutOriginal;
- LocalAppContextSwitches.s_legacyVarTimeZeroScaleBehaviour = _legacyVarTimeZeroScaleBehaviourOriginal;
- LocalAppContextSwitches.s_useCompatibilityProcessSni = _useCompatibilityProcessSniOriginal;
- LocalAppContextSwitches.s_useCompatibilityAsyncBehaviour = _useCompatibilityAsyncBehaviourOriginal;
- LocalAppContextSwitches.s_useConnectionPoolV2 = _useConnectionPoolV2Original;
- LocalAppContextSwitches.s_truncateScaledDecimal = _truncateScaledDecimalOriginal;
- LocalAppContextSwitches.s_ignoreServerProvidedFailoverPartner = _ignoreServerProvidedFailoverPartnerOriginal;
- LocalAppContextSwitches.s_enableUserAgent = _enableUserAgentOriginal;
- LocalAppContextSwitches.s_multiSubnetFailoverByDefault = _multiSubnetFailoverByDefaultOriginal;
+ LocalAppContextSwitches.s_legacyRowVersionNullBehavior =
+ Convert(_legacyRowVersionNullBehaviorOriginal);
+ LocalAppContextSwitches.s_suppressInsecureTlsWarning =
+ Convert(_suppressInsecureTlsWarningOriginal);
+ LocalAppContextSwitches.s_makeReadAsyncBlocking =
+ Convert(_makeReadAsyncBlockingOriginal);
+ LocalAppContextSwitches.s_useMinimumLoginTimeout =
+ Convert(_useMinimumLoginTimeoutOriginal);
+ LocalAppContextSwitches.s_legacyVarTimeZeroScaleBehaviour =
+ Convert(_legacyVarTimeZeroScaleBehaviourOriginal);
+ LocalAppContextSwitches.s_useCompatibilityProcessSni =
+ Convert(_useCompatibilityProcessSniOriginal);
+ LocalAppContextSwitches.s_useCompatibilityAsyncBehaviour =
+ Convert(_useCompatibilityAsyncBehaviourOriginal);
+ LocalAppContextSwitches.s_useConnectionPoolV2 =
+ Convert(_useConnectionPoolV2Original);
+ LocalAppContextSwitches.s_truncateScaledDecimal =
+ Convert(_truncateScaledDecimalOriginal);
+ LocalAppContextSwitches.s_ignoreServerProvidedFailoverPartner =
+ Convert(_ignoreServerProvidedFailoverPartnerOriginal);
+ LocalAppContextSwitches.s_enableUserAgent =
+ Convert(_enableUserAgentOriginal);
+ LocalAppContextSwitches.s_multiSubnetFailoverByDefault =
+ Convert(_multiSubnetFailoverByDefaultOriginal);
#if NET
- LocalAppContextSwitches.s_globalizationInvariantMode = _globalizationInvariantModeOriginal;
+ LocalAppContextSwitches.s_globalizationInvariantMode =
+ Convert(_globalizationInvariantModeOriginal);
#endif
#if NET && _WINDOWS
- LocalAppContextSwitches.s_useManagedNetworking = _useManagedNetworkingOriginal;
+ LocalAppContextSwitches.s_useManagedNetworking =
+ Convert(_useManagedNetworkingOriginal);
#endif
#if NETFRAMEWORK
- LocalAppContextSwitches.s_disableTnirByDefault = _disableTnirByDefaultOriginal;
+ LocalAppContextSwitches.s_disableTnirByDefault =
+ Convert(_disableTnirByDefaultOriginal);
#endif
// Release the lock to allow another instance to be created.
@@ -262,8 +292,8 @@ public bool DisableTnirByDefault
///
public bool? LegacyRowVersionNullBehaviorValue
{
- get => LocalAppContextSwitches.s_legacyRowVersionNullBehavior;
- set => LocalAppContextSwitches.s_legacyRowVersionNullBehavior = value;
+ get => Convert(LocalAppContextSwitches.s_legacyRowVersionNullBehavior);
+ set => LocalAppContextSwitches.s_legacyRowVersionNullBehavior = Convert(value);
}
///
@@ -272,8 +302,8 @@ public bool? LegacyRowVersionNullBehaviorValue
///
public bool? SuppressInsecureTlsWarningValue
{
- get => LocalAppContextSwitches.s_suppressInsecureTlsWarning;
- set => LocalAppContextSwitches.s_suppressInsecureTlsWarning = value;
+ get => Convert(LocalAppContextSwitches.s_suppressInsecureTlsWarning);
+ set => LocalAppContextSwitches.s_suppressInsecureTlsWarning = Convert(value);
}
///
@@ -282,8 +312,8 @@ public bool? SuppressInsecureTlsWarningValue
///
public bool? MakeReadAsyncBlockingValue
{
- get => LocalAppContextSwitches.s_makeReadAsyncBlocking;
- set => LocalAppContextSwitches.s_makeReadAsyncBlocking = value;
+ get => Convert(LocalAppContextSwitches.s_makeReadAsyncBlocking);
+ set => LocalAppContextSwitches.s_makeReadAsyncBlocking = Convert(value);
}
///
@@ -292,8 +322,8 @@ public bool? MakeReadAsyncBlockingValue
///
public bool? UseMinimumLoginTimeoutValue
{
- get => LocalAppContextSwitches.s_useMinimumLoginTimeout;
- set => LocalAppContextSwitches.s_useMinimumLoginTimeout = value;
+ get => Convert(LocalAppContextSwitches.s_useMinimumLoginTimeout);
+ set => LocalAppContextSwitches.s_useMinimumLoginTimeout = Convert(value);
}
///
@@ -302,8 +332,8 @@ public bool? UseMinimumLoginTimeoutValue
///
public bool? LegacyVarTimeZeroScaleBehaviourValue
{
- get => LocalAppContextSwitches.s_legacyVarTimeZeroScaleBehaviour;
- set => LocalAppContextSwitches.s_legacyVarTimeZeroScaleBehaviour = value;
+ get => Convert(LocalAppContextSwitches.s_legacyVarTimeZeroScaleBehaviour);
+ set => LocalAppContextSwitches.s_legacyVarTimeZeroScaleBehaviour = Convert(value);
}
///
@@ -312,8 +342,8 @@ public bool? LegacyVarTimeZeroScaleBehaviourValue
///
public bool? UseCompatibilityProcessSniValue
{
- get => LocalAppContextSwitches.s_useCompatibilityProcessSni;
- set => LocalAppContextSwitches.s_useCompatibilityProcessSni = value;
+ get => Convert(LocalAppContextSwitches.s_useCompatibilityProcessSni);
+ set => LocalAppContextSwitches.s_useCompatibilityProcessSni = Convert(value);
}
///
@@ -322,8 +352,8 @@ public bool? UseCompatibilityProcessSniValue
///
public bool? UseCompatibilityAsyncBehaviourValue
{
- get => LocalAppContextSwitches.s_useCompatibilityAsyncBehaviour;
- set => LocalAppContextSwitches.s_useCompatibilityAsyncBehaviour = value;
+ get => Convert(LocalAppContextSwitches.s_useCompatibilityAsyncBehaviour);
+ set => LocalAppContextSwitches.s_useCompatibilityAsyncBehaviour = Convert(value);
}
///
@@ -331,8 +361,8 @@ public bool? UseCompatibilityAsyncBehaviourValue
///
public bool? UseConnectionPoolV2Value
{
- get => LocalAppContextSwitches.s_useConnectionPoolV2;
- set => LocalAppContextSwitches.s_useConnectionPoolV2 = value;
+ get => Convert(LocalAppContextSwitches.s_useConnectionPoolV2);
+ set => LocalAppContextSwitches.s_useConnectionPoolV2 = Convert(value);
}
///
@@ -340,26 +370,26 @@ public bool? UseConnectionPoolV2Value
///
public bool? TruncateScaledDecimalValue
{
- get => LocalAppContextSwitches.s_truncateScaledDecimal;
- set => LocalAppContextSwitches.s_truncateScaledDecimal = value;
+ get => Convert(LocalAppContextSwitches.s_truncateScaledDecimal);
+ set => LocalAppContextSwitches.s_truncateScaledDecimal = Convert(value);
}
public bool? IgnoreServerProvidedFailoverPartnerValue
{
- get => LocalAppContextSwitches.s_ignoreServerProvidedFailoverPartner;
- set => LocalAppContextSwitches.s_ignoreServerProvidedFailoverPartner = value;
+ get => Convert(LocalAppContextSwitches.s_ignoreServerProvidedFailoverPartner);
+ set => LocalAppContextSwitches.s_ignoreServerProvidedFailoverPartner = Convert(value);
}
public bool? EnableUserAgentValue
{
- get => LocalAppContextSwitches.s_enableUserAgent;
- set => LocalAppContextSwitches.s_enableUserAgent = value;
+ get => Convert(LocalAppContextSwitches.s_enableUserAgent);
+ set => LocalAppContextSwitches.s_enableUserAgent = Convert(value);
}
public bool? EnableMultiSubnetFailoverByDefaultValue
{
- get => LocalAppContextSwitches.s_multiSubnetFailoverByDefault;
- set => LocalAppContextSwitches.s_multiSubnetFailoverByDefault = value;
+ get => Convert(LocalAppContextSwitches.s_multiSubnetFailoverByDefault);
+ set => LocalAppContextSwitches.s_multiSubnetFailoverByDefault = Convert(value);
}
#if NET
@@ -368,8 +398,8 @@ public bool? EnableMultiSubnetFailoverByDefaultValue
///
public bool? GlobalizationInvariantModeValue
{
- get => LocalAppContextSwitches.s_globalizationInvariantMode;
- set => LocalAppContextSwitches.s_globalizationInvariantMode = value;
+ get => Convert(LocalAppContextSwitches.s_globalizationInvariantMode);
+ set => LocalAppContextSwitches.s_globalizationInvariantMode = Convert(value);
}
#endif
@@ -379,8 +409,8 @@ public bool? GlobalizationInvariantModeValue
///
public bool? UseManagedNetworkingValue
{
- get => LocalAppContextSwitches.s_useManagedNetworking;
- set => LocalAppContextSwitches.s_useManagedNetworking = value;
+ get => Convert(LocalAppContextSwitches.s_useManagedNetworking);
+ set => LocalAppContextSwitches.s_useManagedNetworking = Convert(value);
}
#endif
@@ -391,10 +421,30 @@ public bool? UseManagedNetworkingValue
///
public bool? DisableTnirByDefaultValue
{
- get => LocalAppContextSwitches.s_disableTnirByDefault;
- set => LocalAppContextSwitches.s_disableTnirByDefault = value;
+ get => Convert(LocalAppContextSwitches.s_disableTnirByDefault);
+ set => LocalAppContextSwitches.s_disableTnirByDefault = Convert(value);
}
#endif
#endregion
+
+ #region Helpers
+
+ private static bool? Convert(LocalAppContextSwitches.SwitchValue value)
+ {
+ return value == LocalAppContextSwitches.SwitchValue.None
+ ? null
+ : value == LocalAppContextSwitches.SwitchValue.True;
+ }
+
+ private static LocalAppContextSwitches.SwitchValue Convert(bool? value)
+ {
+ return !value.HasValue
+ ? LocalAppContextSwitches.SwitchValue.None
+ : value.Value
+ ? LocalAppContextSwitches.SwitchValue.True
+ : LocalAppContextSwitches.SwitchValue.False;
+ }
+
+ #endregion
}
From 6bf4eaadfdf1f41142b01b5f358cc4b66ee06bc9 Mon Sep 17 00:00:00 2001
From: Paul Medynski <31868385+paulmedynski@users.noreply.github.com>
Date: Thu, 18 Dec 2025 13:34:58 -0400
Subject: [PATCH 3/7] - Reverted to reflection since the Common project is used
by other test projects that don't have access to MDS internals.
---
.../src/Microsoft.Data.SqlClient.csproj | 1 -
.../netfx/src/Microsoft.Data.SqlClient.csproj | 3 -
.../Data/SqlClient/LocalAppContextSwitches.cs | 12 +-
.../Common/LocalAppContextSwitchesHelper.cs | 518 +++++++++---------
.../tests/FunctionalTests/SqlParameterTest.cs | 2 +-
.../TdsParserStateObject.TestHarness.cs | 24 +-
.../SQL/DataReaderTest/DataReaderTest.cs | 4 +-
.../SQL/ParameterTest/ParametersTest.cs | 6 +-
.../AdjustPrecScaleForBulkCopy.cs | 4 +-
.../SqlClient/LocalAppContextSwitchesTest.cs | 15 +-
.../Data/SqlClient/SqlConnectionStringTest.cs | 23 +-
.../ConnectionFailoverTests.cs | 2 +-
.../SimulatedServerTests/ConnectionTests.cs | 4 +-
13 files changed, 313 insertions(+), 305 deletions(-)
diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj
index 512db7418e..f8fc03f915 100644
--- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj
+++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj
@@ -32,7 +32,6 @@
$([System.IO.Path]::Combine('$(IntermediateOutputPath)','$(TargetFramework)','$(TargetFrameworkMoniker).AssemblyAttributes$(DefaultLanguageSourceExtension)'))
-
diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj
index bc14696009..e0974d0d44 100644
--- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj
+++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj
@@ -33,9 +33,6 @@
<_Parameter1>UnitTests
-
- <_Parameter1>Common
-
diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/LocalAppContextSwitches.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/LocalAppContextSwitches.cs
index e01422a875..a32ae5ba8e 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/LocalAppContextSwitches.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/LocalAppContextSwitches.cs
@@ -70,7 +70,7 @@ internal enum SwitchValue : byte
internal static SwitchValue s_globalizationInvariantMode;
#endif
#if NET && _WINDOWS
- internal static TriState s_useManagedNetworking;
+ internal static SwitchValue s_useManagedNetworking;
#endif
#if NETFRAMEWORK
internal static SwitchValue s_disableTnirByDefault;
@@ -467,22 +467,22 @@ public static bool UseManagedNetworking
{
get
{
- if (s_useManagedNetworking == TriState.None)
+ if (s_useManagedNetworking == SwitchValue.None)
{
if (!OperatingSystem.IsWindows())
{
- s_useManagedNetworking = TriState.True;
+ s_useManagedNetworking = SwitchValue.True;
}
else if (AppContext.TryGetSwitch(UseManagedNetworkingOnWindowsString, out bool returnedValue) && returnedValue)
{
- s_useManagedNetworking = TriState.True;
+ s_useManagedNetworking = SwitchValue.True;
}
else
{
- s_useManagedNetworking = TriState.False;
+ s_useManagedNetworking = SwitchValue.False;
}
}
- return s_useManagedNetworking == TriState.True;
+ return s_useManagedNetworking == SwitchValue.True;
}
}
#else
diff --git a/src/Microsoft.Data.SqlClient/tests/Common/LocalAppContextSwitchesHelper.cs b/src/Microsoft.Data.SqlClient/tests/Common/LocalAppContextSwitchesHelper.cs
index 5182e7b7e7..7919ecb8f9 100644
--- a/src/Microsoft.Data.SqlClient/tests/Common/LocalAppContextSwitchesHelper.cs
+++ b/src/Microsoft.Data.SqlClient/tests/Common/LocalAppContextSwitchesHelper.cs
@@ -1,31 +1,40 @@
using System;
+using System.Reflection;
using System.Threading;
namespace Microsoft.Data.SqlClient.Tests.Common;
///
-/// This class provides read/write access to LocalAppContextSwitches values
-/// for the duration of a test. It is intended to be constructed at the start
-/// of a test and disposed of at the end. It captures the original values of
-/// the switches and restores them when disposed.
+/// This class provides read/write access to LocalAppContextSwitches values for
+/// the duration of a test. It is intended to be constructed at the start of a
+/// test and disposed of at the end. It captures the original values of the
+/// switches and restores them when disposed.
///
/// This follows the RAII pattern to ensure that the switches are always
/// restored, which is important for global state like LocalAppContextSwitches.
///
/// https://en.wikipedia.org/wiki/Resource_acquisition_is_initialization
-///
-/// Only one instance can exist at a time. Overlapping constructor calls will
-/// wait until the previous instance is disposed.
+///
+/// As with all global state, care must be taken when using this class in tests
+/// that may run in parallel. This class enforces a single instance policy
+/// using a semaphore. Overlapping constructor calls will wait up to 5 seconds
+/// for the previous instance to be disposed. Any tests that use this class
+/// should not keep an instance alive for longer than 5 seconds, or they risk
+/// causing failures in other tests.
///
public sealed class LocalAppContextSwitchesHelper : IDisposable
{
#region Private Fields
- // This semaphore ensures that only one instance of this class may exist at
- // a time.
+ ///
+ /// This semaphore ensures that only one instance of this class may exist at
+ /// a time.
+ ///
private static readonly SemaphoreSlim s_instanceLock = new(1, 1);
- // These fields are used to capture the original switch values.
+ ///
+ /// These fields are used to capture the original switch values.
+ ///
private readonly bool? _legacyRowVersionNullBehaviorOriginal;
private readonly bool? _suppressInsecureTlsWarningOriginal;
private readonly bool? _makeReadAsyncBlockingOriginal;
@@ -74,42 +83,52 @@ public LocalAppContextSwitchesHelper()
"instance to be disposed.");
}
- _legacyRowVersionNullBehaviorOriginal =
- Convert(LocalAppContextSwitches.s_legacyRowVersionNullBehavior);
- _suppressInsecureTlsWarningOriginal =
- Convert(LocalAppContextSwitches.s_suppressInsecureTlsWarning);
- _makeReadAsyncBlockingOriginal =
- Convert(LocalAppContextSwitches.s_makeReadAsyncBlocking);
- _useMinimumLoginTimeoutOriginal =
- Convert(LocalAppContextSwitches.s_useMinimumLoginTimeout);
- _legacyVarTimeZeroScaleBehaviourOriginal =
- Convert(LocalAppContextSwitches.s_legacyVarTimeZeroScaleBehaviour);
- _useCompatibilityProcessSniOriginal =
- Convert(LocalAppContextSwitches.s_useCompatibilityProcessSni);
- _useCompatibilityAsyncBehaviourOriginal =
- Convert(LocalAppContextSwitches.s_useCompatibilityAsyncBehaviour);
- _useConnectionPoolV2Original =
- Convert(LocalAppContextSwitches.s_useConnectionPoolV2);
- _truncateScaledDecimalOriginal =
- Convert(LocalAppContextSwitches.s_truncateScaledDecimal);
- _ignoreServerProvidedFailoverPartnerOriginal =
- Convert(LocalAppContextSwitches.s_ignoreServerProvidedFailoverPartner);
- _enableUserAgentOriginal =
- Convert(LocalAppContextSwitches.s_enableUserAgent);
- _multiSubnetFailoverByDefaultOriginal =
- Convert(LocalAppContextSwitches.s_multiSubnetFailoverByDefault);
- #if NET
- _globalizationInvariantModeOriginal =
- Convert(LocalAppContextSwitches.s_globalizationInvariantMode);
- #endif
- #if NET && _WINDOWS
- _useManagedNetworkingOriginal =
- Convert(LocalAppContextSwitches.s_useManagedNetworking);
- #endif
- #if NETFRAMEWORK
- _disableTnirByDefaultOriginal =
- Convert(LocalAppContextSwitches.s_disableTnirByDefault);
- #endif
+ try
+ {
+ _legacyRowVersionNullBehaviorOriginal =
+ GetSwitchValue("s_legacyRowVersionNullBehavior");
+ _suppressInsecureTlsWarningOriginal =
+ GetSwitchValue("s_suppressInsecureTlsWarning");
+ _makeReadAsyncBlockingOriginal =
+ GetSwitchValue("s_makeReadAsyncBlocking");
+ _useMinimumLoginTimeoutOriginal =
+ GetSwitchValue("s_useMinimumLoginTimeout");
+ _legacyVarTimeZeroScaleBehaviourOriginal =
+ GetSwitchValue("s_legacyVarTimeZeroScaleBehaviour");
+ _useCompatibilityProcessSniOriginal =
+ GetSwitchValue("s_useCompatibilityProcessSni");
+ _useCompatibilityAsyncBehaviourOriginal =
+ GetSwitchValue("s_useCompatibilityAsyncBehaviour");
+ _useConnectionPoolV2Original =
+ GetSwitchValue("s_useConnectionPoolV2");
+ _truncateScaledDecimalOriginal =
+ GetSwitchValue("s_truncateScaledDecimal");
+ _ignoreServerProvidedFailoverPartnerOriginal =
+ GetSwitchValue("s_ignoreServerProvidedFailoverPartner");
+ _enableUserAgentOriginal =
+ GetSwitchValue("s_enableUserAgent");
+ _multiSubnetFailoverByDefaultOriginal =
+ GetSwitchValue("s_multiSubnetFailoverByDefault");
+ #if NET
+ _globalizationInvariantModeOriginal =
+ GetSwitchValue("s_globalizationInvariantMode");
+ #endif
+ #if NET && _WINDOWS
+ _useManagedNetworkingOriginal =
+ GetSwitchValue("s_useManagedNetworking");
+ #endif
+ #if NETFRAMEWORK
+ _disableTnirByDefaultOriginal =
+ GetSwitchValue("s_disableTnirByDefault");
+ #endif
+ }
+ catch
+ {
+ // If we fail to capture the original values, release the lock
+ // immediately to avoid deadlocks.
+ s_instanceLock.Release();
+ throw;
+ }
}
///
@@ -117,289 +136,189 @@ public LocalAppContextSwitchesHelper()
///
public void Dispose()
{
- LocalAppContextSwitches.s_legacyRowVersionNullBehavior =
- Convert(_legacyRowVersionNullBehaviorOriginal);
- LocalAppContextSwitches.s_suppressInsecureTlsWarning =
- Convert(_suppressInsecureTlsWarningOriginal);
- LocalAppContextSwitches.s_makeReadAsyncBlocking =
- Convert(_makeReadAsyncBlockingOriginal);
- LocalAppContextSwitches.s_useMinimumLoginTimeout =
- Convert(_useMinimumLoginTimeoutOriginal);
- LocalAppContextSwitches.s_legacyVarTimeZeroScaleBehaviour =
- Convert(_legacyVarTimeZeroScaleBehaviourOriginal);
- LocalAppContextSwitches.s_useCompatibilityProcessSni =
- Convert(_useCompatibilityProcessSniOriginal);
- LocalAppContextSwitches.s_useCompatibilityAsyncBehaviour =
- Convert(_useCompatibilityAsyncBehaviourOriginal);
- LocalAppContextSwitches.s_useConnectionPoolV2 =
- Convert(_useConnectionPoolV2Original);
- LocalAppContextSwitches.s_truncateScaledDecimal =
- Convert(_truncateScaledDecimalOriginal);
- LocalAppContextSwitches.s_ignoreServerProvidedFailoverPartner =
- Convert(_ignoreServerProvidedFailoverPartnerOriginal);
- LocalAppContextSwitches.s_enableUserAgent =
- Convert(_enableUserAgentOriginal);
- LocalAppContextSwitches.s_multiSubnetFailoverByDefault =
- Convert(_multiSubnetFailoverByDefaultOriginal);
- #if NET
- LocalAppContextSwitches.s_globalizationInvariantMode =
- Convert(_globalizationInvariantModeOriginal);
- #endif
- #if NET && _WINDOWS
- LocalAppContextSwitches.s_useManagedNetworking =
- Convert(_useManagedNetworkingOriginal);
- #endif
- #if NETFRAMEWORK
- LocalAppContextSwitches.s_disableTnirByDefault =
- Convert(_disableTnirByDefaultOriginal);
- #endif
-
- // Release the lock to allow another instance to be created.
- s_instanceLock.Release();
+ try
+ {
+ SetSwitchValue(
+ "s_legacyRowVersionNullBehavior",
+ _legacyRowVersionNullBehaviorOriginal);
+ SetSwitchValue(
+ "s_suppressInsecureTlsWarning",
+ _suppressInsecureTlsWarningOriginal);
+ SetSwitchValue(
+ "s_makeReadAsyncBlocking",
+ _makeReadAsyncBlockingOriginal);
+ SetSwitchValue(
+ "s_useMinimumLoginTimeout",
+ _useMinimumLoginTimeoutOriginal);
+ SetSwitchValue(
+ "s_legacyVarTimeZeroScaleBehaviour",
+ _legacyVarTimeZeroScaleBehaviourOriginal);
+ SetSwitchValue(
+ "s_useCompatibilityProcessSni",
+ _useCompatibilityProcessSniOriginal);
+ SetSwitchValue(
+ "s_useCompatibilityAsyncBehaviour",
+ _useCompatibilityAsyncBehaviourOriginal);
+ SetSwitchValue(
+ "s_useConnectionPoolV2",
+ _useConnectionPoolV2Original);
+ SetSwitchValue(
+ "s_truncateScaledDecimal",
+ _truncateScaledDecimalOriginal);
+ SetSwitchValue(
+ "s_ignoreServerProvidedFailoverPartner",
+ _ignoreServerProvidedFailoverPartnerOriginal);
+ SetSwitchValue(
+ "s_enableUserAgent",
+ _enableUserAgentOriginal);
+ SetSwitchValue(
+ "s_multiSubnetFailoverByDefault",
+ _multiSubnetFailoverByDefaultOriginal);
+ #if NET
+ SetSwitchValue(
+ "s_globalizationInvariantMode",
+ _globalizationInvariantModeOriginal);
+ #endif
+ #if NET && _WINDOWS
+ SetSwitchValue(
+ "s_useManagedNetworking",
+ _useManagedNetworkingOriginal);
+ #endif
+ #if NETFRAMEWORK
+ SetSwitchValue(
+ "s_disableTnirByDefault",
+ _disableTnirByDefaultOriginal);
+ #endif
+ }
+ finally
+ {
+ // Release the lock to allow another instance to be created.
+ s_instanceLock.Release();
+ }
}
#endregion
- #region Public Properties
-
- ///
- /// Access the LocalAppContextSwitches.LegacyRowVersionNullBehavior
- /// property.
- ///
- public bool LegacyRowVersionNullBehavior
- {
- get => LocalAppContextSwitches.LegacyRowVersionNullBehavior;
- }
-
- ///
- /// Access the LocalAppContextSwitches.SuppressInsecureTlsWarning property.
- ///
- public bool SuppressInsecureTlsWarning
- {
- get => LocalAppContextSwitches.SuppressInsecureTlsWarning;
- }
-
- ///
- /// Access the LocalAppContextSwitches.MakeReadAsyncBlocking property.
- ///
- public bool MakeReadAsyncBlocking
- {
- get => LocalAppContextSwitches.MakeReadAsyncBlocking;
- }
-
- ///
- /// Access the LocalAppContextSwitches.UseMinimumLoginTimeout property.
- ///
- public bool UseMinimumLoginTimeout
- {
- get => LocalAppContextSwitches.UseMinimumLoginTimeout;
- }
-
- ///
- /// Access the LocalAppContextSwitches.LegacyVarTimeZeroScaleBehaviour
- /// property.
- ///
- public bool LegacyVarTimeZeroScaleBehaviour
- {
- get => LocalAppContextSwitches.LegacyVarTimeZeroScaleBehaviour;
- }
-
- ///
- /// Access the LocalAppContextSwitches.UseCompatibilityProcessSni property.
- ///
- public bool UseCompatibilityProcessSni
- {
- get => LocalAppContextSwitches.UseCompatibilityProcessSni;
- }
-
- ///
- /// Access the LocalAppContextSwitches.UseCompatibilityAsyncBehaviour
- /// property.
- ///
- public bool UseCompatibilityAsyncBehaviour
- {
- get => LocalAppContextSwitches.UseCompatibilityAsyncBehaviour;
- }
-
- ///
- /// Access the LocalAppContextSwitches.UseConnectionPoolV2 property.
- ///
- public bool UseConnectionPoolV2
- {
- get => LocalAppContextSwitches.UseConnectionPoolV2;
- }
-
- ///
- /// Access the LocalAppContextSwitches.TruncateScaledDecimal property.
- ///
- public bool TruncateScaledDecimal
- {
- get => LocalAppContextSwitches.TruncateScaledDecimal;
- }
-
- public bool IgnoreServerProvidedFailoverPartner
- {
- get => LocalAppContextSwitches.IgnoreServerProvidedFailoverPartner;
- }
-
- public bool EnableUserAgent
- {
- get => LocalAppContextSwitches.EnableUserAgent;
- }
-
- public bool EnableMultiSubnetFailoverByDefault
- {
- get => LocalAppContextSwitches.EnableMultiSubnetFailoverByDefault;
- }
-
- #if NET
- ///
- /// Access the LocalAppContextSwitches.GlobalizationInvariantMode property.
- ///
- public bool GlobalizationInvariantMode
- {
- get => LocalAppContextSwitches.GlobalizationInvariantMode;
- }
- #endif
-
- #if NET && _WINDOWS
- ///
- /// Access the LocalAppContextSwitches.UseManagedNetworking property.
- ///
- public bool UseManagedNetworking
- {
- get => LocalAppContextSwitches.UseManagedNetworking;
- }
- #endif
-
- #if NETFRAMEWORK
- ///
- /// Access the LocalAppContextSwitches.DisableTnirByDefault property.
- ///
- public bool DisableTnirByDefault
- {
- get => LocalAppContextSwitches.DisableTnirByDefault;
- }
- #endif
+ #region Switch Value Getters and Setters
// These properties get or set the like-named underlying switch field value.
//
- // They all fail the test if the value cannot be retrieved or set.
+ // They all throw if the value cannot be retrieved or set.
///
/// Get or set the LocalAppContextSwitches.LegacyRowVersionNullBehavior
/// switch value.
///
- public bool? LegacyRowVersionNullBehaviorValue
+ public bool? LegacyRowVersionNullBehavior
{
- get => Convert(LocalAppContextSwitches.s_legacyRowVersionNullBehavior);
- set => LocalAppContextSwitches.s_legacyRowVersionNullBehavior = Convert(value);
+ get => GetSwitchValue("s_legacyRowVersionNullBehavior");
+ set => SetSwitchValue("s_legacyRowVersionNullBehavior", value);
}
///
/// Get or set the LocalAppContextSwitches.SuppressInsecureTlsWarning
/// switch value.
///
- public bool? SuppressInsecureTlsWarningValue
+ public bool? SuppressInsecureTlsWarning
{
- get => Convert(LocalAppContextSwitches.s_suppressInsecureTlsWarning);
- set => LocalAppContextSwitches.s_suppressInsecureTlsWarning = Convert(value);
+ get => GetSwitchValue("s_suppressInsecureTlsWarning");
+ set => SetSwitchValue("s_suppressInsecureTlsWarning", value);
}
///
/// Get or set the LocalAppContextSwitches.MakeReadAsyncBlocking switch
/// value.
///
- public bool? MakeReadAsyncBlockingValue
+ public bool? MakeReadAsyncBlocking
{
- get => Convert(LocalAppContextSwitches.s_makeReadAsyncBlocking);
- set => LocalAppContextSwitches.s_makeReadAsyncBlocking = Convert(value);
+ get => GetSwitchValue("s_makeReadAsyncBlocking");
+ set => SetSwitchValue("s_makeReadAsyncBlocking", value);
}
///
/// Get or set the LocalAppContextSwitches.UseMinimumLoginTimeout switch
/// value.
///
- public bool? UseMinimumLoginTimeoutValue
+ public bool? UseMinimumLoginTimeout
{
- get => Convert(LocalAppContextSwitches.s_useMinimumLoginTimeout);
- set => LocalAppContextSwitches.s_useMinimumLoginTimeout = Convert(value);
+ get => GetSwitchValue("s_useMinimumLoginTimeout");
+ set => SetSwitchValue("s_useMinimumLoginTimeout", value);
}
///
/// Get or set the LocalAppContextSwitches.LegacyVarTimeZeroScaleBehaviour
/// switch value.
///
- public bool? LegacyVarTimeZeroScaleBehaviourValue
+ public bool? LegacyVarTimeZeroScaleBehaviour
{
- get => Convert(LocalAppContextSwitches.s_legacyVarTimeZeroScaleBehaviour);
- set => LocalAppContextSwitches.s_legacyVarTimeZeroScaleBehaviour = Convert(value);
+ get => GetSwitchValue("s_legacyVarTimeZeroScaleBehaviour");
+ set => SetSwitchValue("s_legacyVarTimeZeroScaleBehaviour", value);
}
///
/// Get or set the LocalAppContextSwitches.UseCompatibilityProcessSni switch
/// value.
///
- public bool? UseCompatibilityProcessSniValue
+ public bool? UseCompatibilityProcessSni
{
- get => Convert(LocalAppContextSwitches.s_useCompatibilityProcessSni);
- set => LocalAppContextSwitches.s_useCompatibilityProcessSni = Convert(value);
+ get => GetSwitchValue("s_useCompatibilityProcessSni");
+ set => SetSwitchValue("s_useCompatibilityProcessSni", value);
}
///
/// Get or set the LocalAppContextSwitches.UseCompatibilityAsyncBehaviour
/// switch value.
///
- public bool? UseCompatibilityAsyncBehaviourValue
+ public bool? UseCompatibilityAsyncBehaviour
{
- get => Convert(LocalAppContextSwitches.s_useCompatibilityAsyncBehaviour);
- set => LocalAppContextSwitches.s_useCompatibilityAsyncBehaviour = Convert(value);
+ get => GetSwitchValue("s_useCompatibilityAsyncBehaviour");
+ set => SetSwitchValue("s_useCompatibilityAsyncBehaviour", value);
}
///
/// Get or set the LocalAppContextSwitches.UseConnectionPoolV2 switch value.
///
- public bool? UseConnectionPoolV2Value
+ public bool? UseConnectionPoolV2
{
- get => Convert(LocalAppContextSwitches.s_useConnectionPoolV2);
- set => LocalAppContextSwitches.s_useConnectionPoolV2 = Convert(value);
+ get => GetSwitchValue("s_useConnectionPoolV2");
+ set => SetSwitchValue("s_useConnectionPoolV2", value);
}
///
/// Get or set the LocalAppContextSwitches.TruncateScaledDecimal switch value.
///
- public bool? TruncateScaledDecimalValue
+ public bool? TruncateScaledDecimal
{
- get => Convert(LocalAppContextSwitches.s_truncateScaledDecimal);
- set => LocalAppContextSwitches.s_truncateScaledDecimal = Convert(value);
+ get => GetSwitchValue("s_truncateScaledDecimal");
+ set => SetSwitchValue("s_truncateScaledDecimal", value);
}
- public bool? IgnoreServerProvidedFailoverPartnerValue
+ public bool? IgnoreServerProvidedFailoverPartner
{
- get => Convert(LocalAppContextSwitches.s_ignoreServerProvidedFailoverPartner);
- set => LocalAppContextSwitches.s_ignoreServerProvidedFailoverPartner = Convert(value);
+ get => GetSwitchValue("s_ignoreServerProvidedFailoverPartner");
+ set => SetSwitchValue("s_ignoreServerProvidedFailoverPartner", value);
}
- public bool? EnableUserAgentValue
+ public bool? EnableUserAgent
{
- get => Convert(LocalAppContextSwitches.s_enableUserAgent);
- set => LocalAppContextSwitches.s_enableUserAgent = Convert(value);
+ get => GetSwitchValue("s_enableUserAgent");
+ set => SetSwitchValue("s_enableUserAgent", value);
}
- public bool? EnableMultiSubnetFailoverByDefaultValue
+ public bool? EnableMultiSubnetFailoverByDefault
{
- get => Convert(LocalAppContextSwitches.s_multiSubnetFailoverByDefault);
- set => LocalAppContextSwitches.s_multiSubnetFailoverByDefault = Convert(value);
+ get => GetSwitchValue("s_multiSubnetFailoverByDefault");
+ set => SetSwitchValue("s_multiSubnetFailoverByDefault", value);
}
#if NET
///
/// Get or set the LocalAppContextSwitches.GlobalizationInvariantMode switch value.
///
- public bool? GlobalizationInvariantModeValue
+ public bool? GlobalizationInvariantMode
{
- get => Convert(LocalAppContextSwitches.s_globalizationInvariantMode);
- set => LocalAppContextSwitches.s_globalizationInvariantMode = Convert(value);
+ get => GetSwitchValue("s_globalizationInvariantMode");
+ set => SetSwitchValue("s_globalizationInvariantMode", value);
}
#endif
@@ -407,10 +326,10 @@ public bool? GlobalizationInvariantModeValue
///
/// Get or set the LocalAppContextSwitches.UseManagedNetworking switch value.
///
- public bool? UseManagedNetworkingValue
+ public bool? UseManagedNetworking
{
- get => Convert(LocalAppContextSwitches.s_useManagedNetworking);
- set => LocalAppContextSwitches.s_useManagedNetworking = Convert(value);
+ get => GetSwitchValue("s_useManagedNetworking");
+ set => SetSwitchValue("s_useManagedNetworking", value);
}
#endif
@@ -419,10 +338,10 @@ public bool? UseManagedNetworkingValue
/// Get or set the LocalAppContextSwitches.DisableTnirByDefault switch
/// value.
///
- public bool? DisableTnirByDefaultValue
+ public bool? DisableTnirByDefault
{
- get => Convert(LocalAppContextSwitches.s_disableTnirByDefault);
- set => LocalAppContextSwitches.s_disableTnirByDefault = Convert(value);
+ get => GetSwitchValue("s_disableTnirByDefault");
+ set => SetSwitchValue("s_disableTnirByDefault", value);
}
#endif
@@ -430,20 +349,95 @@ public bool? DisableTnirByDefaultValue
#region Helpers
- private static bool? Convert(LocalAppContextSwitches.SwitchValue value)
+ ///
+ /// Use reflection to get a switch field value from LocalAppContextSwitches.
+ ///
+ private static bool? GetSwitchValue(string fieldName)
{
- return value == LocalAppContextSwitches.SwitchValue.None
- ? null
- : value == LocalAppContextSwitches.SwitchValue.True;
+ var assembly = Assembly.GetAssembly(typeof(SqlConnection));
+ if (assembly is null)
+ {
+ throw new InvalidOperationException(
+ "Could not get assembly for Microsoft.Data.SqlClient");
+ }
+
+ var type = assembly.GetType("Microsoft.Data.SqlClient.LocalAppContextSwitches");
+ if (type is null)
+ {
+ throw new InvalidOperationException(
+ "Could not get type LocalAppContextSwitches");
+ }
+
+ var field = type.GetField(
+ fieldName,
+ BindingFlags.Static | BindingFlags.NonPublic);
+ if (field == null)
+ {
+ throw new InvalidOperationException(
+ $"Field '{fieldName}' not found in LocalAppContextSwitches");
+ }
+
+ var value = field.GetValue(null);
+ if (value is byte switchValue)
+ {
+ // GOTCHA: This assumes that switch values map to bytes as:
+ //
+ // None = 0
+ // True = 1
+ // False = 2
+ //
+ // See the LocalAppContextSwitches.SwitchValue enum definition.
+ //
+ return switchValue == 0 ? null : switchValue == 1;
+ }
+
+ throw new InvalidOperationException(
+ $"Field '{fieldName}' is not of type byte");
}
- private static LocalAppContextSwitches.SwitchValue Convert(bool? value)
+ ///
+ /// Use reflection to set a switch field value in LocalAppContextSwitches.
+ ///
+ private static void SetSwitchValue(string fieldName, bool? value)
{
- return !value.HasValue
- ? LocalAppContextSwitches.SwitchValue.None
- : value.Value
- ? LocalAppContextSwitches.SwitchValue.True
- : LocalAppContextSwitches.SwitchValue.False;
+ var assembly = Assembly.GetAssembly(typeof(SqlConnection));
+ if (assembly is null)
+ {
+ throw new InvalidOperationException(
+ "Could not get assembly for Microsoft.Data.SqlClient");
+ }
+
+ var type = assembly.GetType("Microsoft.Data.SqlClient.LocalAppContextSwitches");
+ if (type is null)
+ {
+ throw new InvalidOperationException(
+ "Could not get type LocalAppContextSwitches");
+ }
+
+ var field = type.GetField(
+ fieldName,
+ BindingFlags.Static | BindingFlags.NonPublic);
+ if (field == null)
+ {
+ throw new InvalidOperationException(
+ $"Field '{fieldName}' not found in LocalAppContextSwitches");
+ }
+
+ field.SetValue(
+ null,
+ // GOTCHA: This assumes that switch values map to bytes as:
+ //
+ // None = 0
+ // True = 1
+ // False = 2
+ //
+ // See the LocalAppContextSwitches.SwitchValue enum definition.
+ //
+ !value.HasValue
+ ? (byte)0
+ : value.Value
+ ? (byte)1
+ : (byte)2);
}
#endregion
diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlParameterTest.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlParameterTest.cs
index 8506e35e98..8465d894a9 100644
--- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlParameterTest.cs
+++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlParameterTest.cs
@@ -1952,7 +1952,7 @@ public void SqlDateTime2Scale_Legacy(int? setScale, byte outputScale, bool legac
lock (_parameterLegacyScaleLock)
{
using SwitchesHelper switches = new SwitchesHelper();
- switches.LegacyVarTimeZeroScaleBehaviourValue =
+ switches.LegacyVarTimeZeroScaleBehaviour =
legacyVarTimeZeroScaleSwitchValue;
var parameter = new SqlParameter
diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/TdsParserStateObject.TestHarness.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/TdsParserStateObject.TestHarness.cs
index 6e8f64fcc8..377be3e511 100644
--- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/TdsParserStateObject.TestHarness.cs
+++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/TdsParserStateObject.TestHarness.cs
@@ -5,10 +5,8 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
-using System.Reflection;
using Microsoft.Data.SqlClient.Tests;
-
-using SwitchesHelper = Microsoft.Data.SqlClient.Tests.Common.LocalAppContextSwitchesHelper;
+using Microsoft.Data.SqlClient.Tests.Common;
namespace Microsoft.Data.SqlClient
{
@@ -163,7 +161,25 @@ private void AssertValidState() { }
[DebuggerStepThrough]
private void AddError(object value) => throw new Exception(value as string ?? "AddError");
- private SwitchesHelper LocalAppContextSwitches = new();
+ private class SwitchesHelper : IDisposable
+ {
+ private LocalAppContextSwitchesHelper _helper = new();
+
+ public void Dispose()
+ {
+ _helper.Dispose();
+ }
+
+ public bool UseCompatibilityProcessSni
+ {
+ get
+ {
+ var value = _helper.UseCompatibilityProcessSni;
+ return value.HasValue && value.Value;
+ }
+ }
+ }
+ private SwitchesHelper LocalAppContextSwitches = new SwitchesHelper();
#if NETFRAMEWORK
private SniNativeWrapperImpl _native;
diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataReaderTest/DataReaderTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataReaderTest/DataReaderTest.cs
index 4b7ac38cfa..040119616e 100644
--- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataReaderTest/DataReaderTest.cs
+++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataReaderTest/DataReaderTest.cs
@@ -268,7 +268,7 @@ public static void CheckNullRowVersionIsDBNull()
lock (s_rowVersionLock)
{
using SwitchesHelper helper = new();
- helper.LegacyRowVersionNullBehaviorValue = false;
+ helper.LegacyRowVersionNullBehavior = false;
using SqlConnection con = new(DataTestUtility.TCPConnectionString);
con.Open();
@@ -869,7 +869,7 @@ public static void CheckLegacyNullRowVersionIsEmptyArray()
lock (s_rowVersionLock)
{
using SwitchesHelper helper = new();
- helper.LegacyRowVersionNullBehaviorValue = true;
+ helper.LegacyRowVersionNullBehavior = true;
using SqlConnection con = new(DataTestUtility.TCPConnectionString);
con.Open();
diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/ParametersTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/ParametersTest.cs
index b16368a9c0..4574f81a93 100644
--- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/ParametersTest.cs
+++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/ParametersTest.cs
@@ -589,7 +589,7 @@ public static void TestScaledDecimalParameter_CommandInsert(string connectionStr
{
using (SqlCommand cmd = connection.CreateCommand())
{
- appContextSwitchesHelper.TruncateScaledDecimalValue = truncateScaledDecimal;
+ appContextSwitchesHelper.TruncateScaledDecimal = truncateScaledDecimal;
var p = new SqlParameter("@Value", null)
{
@@ -636,7 +636,7 @@ public static void TestScaledDecimalParameter_BulkCopy(string connectionString,
}
bulkCopy.DestinationTableName = tableName;
- appContextSwitchesHelper.TruncateScaledDecimalValue = truncateScaledDecimal;
+ appContextSwitchesHelper.TruncateScaledDecimal = truncateScaledDecimal;
bulkCopy.WriteToServer(table);
}
Assert.True(ValidateInsertedValues(connection, tableName, truncateScaledDecimal), $"Invalid test happened with connection string [{connection.ConnectionString}]");
@@ -681,7 +681,7 @@ public static void TestScaledDecimalTVP_CommandSP(string connectionString, bool
table.Rows.Add(newRow);
}
p.Value = table;
- appContextSwitchesHelper.TruncateScaledDecimalValue = truncateScaledDecimal;
+ appContextSwitchesHelper.TruncateScaledDecimal = truncateScaledDecimal;
cmd.ExecuteNonQuery();
}
// TVP always rounds data without attention to the configuration.
diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/AdjustPrecScaleForBulkCopy.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/AdjustPrecScaleForBulkCopy.cs
index 5a335e957e..49912202d6 100644
--- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/AdjustPrecScaleForBulkCopy.cs
+++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/AdjustPrecScaleForBulkCopy.cs
@@ -29,7 +29,9 @@ public static void RunTest()
Assert.Equal("12.3", value.ToString());
value = BulkCopySqlDecimalToTable(new SqlDecimal(123.45), 10, 2, 4, 1);
- if (appContextSwitches.TruncateScaledDecimal)
+
+ bool? truncate = appContextSwitches.TruncateScaledDecimal;
+ if (truncate.HasValue && truncate.Value)
{
Assert.Equal("123.4", value.ToString());
}
diff --git a/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/LocalAppContextSwitchesTest.cs b/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/LocalAppContextSwitchesTest.cs
index c92402af41..59445a26b6 100644
--- a/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/LocalAppContextSwitchesTest.cs
+++ b/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/LocalAppContextSwitchesTest.cs
@@ -27,12 +27,17 @@ public void TestDefaultAppContextSwitchValues()
Assert.True(LocalAppContextSwitches.UseCompatibilityAsyncBehaviour);
Assert.False(LocalAppContextSwitches.UseConnectionPoolV2);
Assert.False(LocalAppContextSwitches.TruncateScaledDecimal);
+ Assert.False(LocalAppContextSwitches.IgnoreServerProvidedFailoverPartner);
+ Assert.False(LocalAppContextSwitches.EnableUserAgent);
Assert.False(LocalAppContextSwitches.EnableMultiSubnetFailoverByDefault);
-#if NETFRAMEWORK
- Assert.False(LocalAppContextSwitches.DisableTnirByDefault);
+ #if NET
+ Assert.False(LocalAppContextSwitches.GlobalizationInvariantMode);
+ #endif
+ #if NET && _WINDOWS
Assert.False(LocalAppContextSwitches.UseManagedNetworking);
-#else
- Assert.Equal(!OperatingSystem.IsWindows(), LocalAppContextSwitches.UseManagedNetworking);
-#endif
+ #endif
+ #if NETFRAMEWORK
+ Assert.False(LocalAppContextSwitches.DisableTnirByDefault);
+ #endif
}
}
diff --git a/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlConnectionStringTest.cs b/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlConnectionStringTest.cs
index 6133671881..8c2f879041 100644
--- a/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlConnectionStringTest.cs
+++ b/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlConnectionStringTest.cs
@@ -1,17 +1,18 @@
using System;
using Microsoft.Data.SqlClient.Tests.Common;
using Xunit;
-using static Microsoft.Data.SqlClient.Tests.Common.LocalAppContextSwitchesHelper;
namespace Microsoft.Data.SqlClient.UnitTests.Microsoft.Data.SqlClient
{
public class SqlConnectionStringTest : IDisposable
{
- private LocalAppContextSwitchesHelper _appContextSwitchHelper;
- public SqlConnectionStringTest()
+ // Ensure we restore the original app context switch values after each
+ // test.
+ private LocalAppContextSwitchesHelper _appContextSwitchHelper = new();
+
+ public void Dispose()
{
- // Ensure that the app context switch is set to the default value
- _appContextSwitchHelper = new LocalAppContextSwitchesHelper();
+ _appContextSwitchHelper.Dispose();
}
#if NETFRAMEWORK
@@ -43,7 +44,7 @@ public void TestDefaultTnir(string dataSource, bool? tnirEnabledInConnString, bo
// the value of TransparentNetworkIPResolution property in SqlConnectionString.
// Arrange
- _appContextSwitchHelper.DisableTnirByDefaultValue = tnirDisabledAppContext;
+ _appContextSwitchHelper.DisableTnirByDefault = tnirDisabledAppContext;
// Act
SqlConnectionStringBuilder builder = new();
@@ -71,7 +72,7 @@ public void TestDefaultTnir(string dataSource, bool? tnirEnabledInConnString, bo
[InlineData(null, null, false)]
public void TestDefaultMultiSubnetFailover(bool? msfInConnString, bool? msfEnabledAppContext, bool expectedValue)
{
- _appContextSwitchHelper.EnableMultiSubnetFailoverByDefaultValue = msfEnabledAppContext;
+ _appContextSwitchHelper.EnableMultiSubnetFailoverByDefault = msfEnabledAppContext;
SqlConnectionStringBuilder builder = new();
if (msfInConnString.HasValue)
@@ -89,7 +90,7 @@ public void TestDefaultMultiSubnetFailover(bool? msfInConnString, bool? msfEnabl
[Fact]
public void TestMultiSubnetFailoverWithFailoverPartnerThrows()
{
- _appContextSwitchHelper.EnableMultiSubnetFailoverByDefaultValue = true;
+ _appContextSwitchHelper.EnableMultiSubnetFailoverByDefault = true;
SqlConnectionStringBuilder builder = new()
{
@@ -100,11 +101,5 @@ public void TestMultiSubnetFailoverWithFailoverPartnerThrows()
Assert.Throws(() => new SqlConnectionString(builder.ConnectionString));
}
-
- public void Dispose()
- {
- // Clean up any resources if necessary
- _appContextSwitchHelper.Dispose();
- }
}
}
diff --git a/src/Microsoft.Data.SqlClient/tests/UnitTests/SimulatedServerTests/ConnectionFailoverTests.cs b/src/Microsoft.Data.SqlClient/tests/UnitTests/SimulatedServerTests/ConnectionFailoverTests.cs
index f815a909d8..5b294ff20e 100644
--- a/src/Microsoft.Data.SqlClient/tests/UnitTests/SimulatedServerTests/ConnectionFailoverTests.cs
+++ b/src/Microsoft.Data.SqlClient/tests/UnitTests/SimulatedServerTests/ConnectionFailoverTests.cs
@@ -529,7 +529,7 @@ public void TransientFault_IgnoreServerProvidedFailoverPartner_ShouldConnectToUs
{
// Arrange
using LocalAppContextSwitchesHelper switchesHelper = new();
- switchesHelper.IgnoreServerProvidedFailoverPartnerValue = true;
+ switchesHelper.IgnoreServerProvidedFailoverPartner = true;
using TdsServer failoverServer = new(
new TdsServerArguments
diff --git a/src/Microsoft.Data.SqlClient/tests/UnitTests/SimulatedServerTests/ConnectionTests.cs b/src/Microsoft.Data.SqlClient/tests/UnitTests/SimulatedServerTests/ConnectionTests.cs
index daae6d7598..a6d2cac9fd 100644
--- a/src/Microsoft.Data.SqlClient/tests/UnitTests/SimulatedServerTests/ConnectionTests.cs
+++ b/src/Microsoft.Data.SqlClient/tests/UnitTests/SimulatedServerTests/ConnectionTests.cs
@@ -839,7 +839,7 @@ public void TestConnWithUserAgentFeatureExtension(bool forceAck)
{
// Make sure needed switch is enabled
using LocalAppContextSwitchesHelper switchesHelper = new();
- switchesHelper.EnableUserAgentValue = true;
+ switchesHelper.EnableUserAgent = true;
using var server = new TdsServer();
server.Start();
@@ -935,7 +935,7 @@ public void TestConnWithoutUserAgentFeatureExtension()
{
// Disable the client-side UserAgent field entirely
using LocalAppContextSwitchesHelper switchesHelper = new();
- switchesHelper.EnableUserAgentValue = false;
+ switchesHelper.EnableUserAgent = false;
using var server = new TdsServer();
server.Start();
From aaf5a80acf0daccc3fcdda0035def24e475774bf Mon Sep 17 00:00:00 2001
From: Paul Medynski <31868385+paulmedynski@users.noreply.github.com>
Date: Thu, 18 Dec 2025 14:59:15 -0400
Subject: [PATCH 4/7] - Fixed reflection-based enum value setting.
---
.../ColumnEncryptionCertificateFixture.cs | 2 +-
.../Common/LocalAppContextSwitchesHelper.cs | 32 +++++++++----------
2 files changed, 16 insertions(+), 18 deletions(-)
diff --git a/src/Microsoft.Data.SqlClient/tests/Common/Fixtures/ColumnEncryptionCertificateFixture.cs b/src/Microsoft.Data.SqlClient/tests/Common/Fixtures/ColumnEncryptionCertificateFixture.cs
index 05b54ed091..0c51a7e17b 100644
--- a/src/Microsoft.Data.SqlClient/tests/Common/Fixtures/ColumnEncryptionCertificateFixture.cs
+++ b/src/Microsoft.Data.SqlClient/tests/Common/Fixtures/ColumnEncryptionCertificateFixture.cs
@@ -66,7 +66,7 @@ public X509Certificate2 GetCertificate(StoreLocation storeLocation)
IsAdmin &&
_localMachineCertificate is not null)
{
- return _localMachineCertificate!;
+ return _localMachineCertificate;
}
throw new InvalidOperationException("Attempted to retrieve the certificate added to the local machine store; this requires administrator rights.");
diff --git a/src/Microsoft.Data.SqlClient/tests/Common/LocalAppContextSwitchesHelper.cs b/src/Microsoft.Data.SqlClient/tests/Common/LocalAppContextSwitchesHelper.cs
index 7919ecb8f9..4c77c4dd26 100644
--- a/src/Microsoft.Data.SqlClient/tests/Common/LocalAppContextSwitchesHelper.cs
+++ b/src/Microsoft.Data.SqlClient/tests/Common/LocalAppContextSwitchesHelper.cs
@@ -378,7 +378,7 @@ public bool? DisableTnirByDefault
}
var value = field.GetValue(null);
- if (value is byte switchValue)
+ if (value is not null)
{
// GOTCHA: This assumes that switch values map to bytes as:
//
@@ -388,7 +388,8 @@ public bool? DisableTnirByDefault
//
// See the LocalAppContextSwitches.SwitchValue enum definition.
//
- return switchValue == 0 ? null : switchValue == 1;
+ byte underlyingValue = (byte)value;
+ return underlyingValue == 0 ? null : underlyingValue == 1;
}
throw new InvalidOperationException(
@@ -423,21 +424,18 @@ private static void SetSwitchValue(string fieldName, bool? value)
$"Field '{fieldName}' not found in LocalAppContextSwitches");
}
- field.SetValue(
- null,
- // GOTCHA: This assumes that switch values map to bytes as:
- //
- // None = 0
- // True = 1
- // False = 2
- //
- // See the LocalAppContextSwitches.SwitchValue enum definition.
- //
- !value.HasValue
- ? (byte)0
- : value.Value
- ? (byte)1
- : (byte)2);
+ // GOTCHA: This assumes that switch values map to bytes as:
+ //
+ // None = 0
+ // True = 1
+ // False = 2
+ //
+ // See the LocalAppContextSwitches.SwitchValue enum definition.
+ //
+ byte byteValue =
+ (byte)(!value.HasValue ? 0 : value.Value ? 1 : 2);
+
+ field.SetValue(null, Enum.ToObject(field.FieldType, byteValue));
}
#endregion
From 7a5e8a04970b5a6d36c94894b8fd1fbe4ee53861 Mon Sep 17 00:00:00 2001
From: Paul Medynski <31868385+paulmedynski@users.noreply.github.com>
Date: Thu, 18 Dec 2025 15:47:12 -0400
Subject: [PATCH 5/7] - Consolidated most switch acquisition into a helper to
reduce copy-pasta. - Addressed some Copilot comments.
---
.../Data/SqlClient/LocalAppContextSwitches.cs | 391 +++++++-----------
.../TdsParserStateObject.TestHarness.cs | 6 +-
.../Data/SqlClient/SqlConnectionStringTest.cs | 2 +-
3 files changed, 144 insertions(+), 255 deletions(-)
diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/LocalAppContextSwitches.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/LocalAppContextSwitches.cs
index a32ae5ba8e..35fd0981c5 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/LocalAppContextSwitches.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/LocalAppContextSwitches.cs
@@ -67,13 +67,13 @@ internal enum SwitchValue : byte
internal static SwitchValue s_multiSubnetFailoverByDefault = SwitchValue.None;
#if NET
- internal static SwitchValue s_globalizationInvariantMode;
+ internal static SwitchValue s_globalizationInvariantMode = SwitchValue.None;
#endif
#if NET && _WINDOWS
- internal static SwitchValue s_useManagedNetworking;
+ internal static SwitchValue s_useManagedNetworking; = SwitchValue.None;
#endif
#if NETFRAMEWORK
- internal static SwitchValue s_disableTnirByDefault;
+ internal static SwitchValue s_disableTnirByDefault = SwitchValue.None;
#endif
#endregion
@@ -111,27 +111,11 @@ static LocalAppContextSwitches()
/// When this switch is set to false, the new experimental ProcessSni behavior using
/// the packet multiplexer is enabled.
///
- public static bool UseCompatibilityProcessSni
- {
- get
- {
- if (s_useCompatibilityProcessSni == SwitchValue.None)
-
- {
- // Check if the switch has been set by the AppContext switch directly
- // If it has not been set, we default to true.
- if (!AppContext.TryGetSwitch(UseCompatibilityProcessSniString, out bool returnedValue) || returnedValue)
- {
- s_useCompatibilityProcessSni = SwitchValue.True;
- }
- else
- {
- s_useCompatibilityProcessSni = SwitchValue.False;
- }
- }
- return s_useCompatibilityProcessSni == SwitchValue.True;
- }
- }
+ public static bool UseCompatibilityProcessSni =>
+ AcquireAndReturn(
+ UseCompatibilityProcessSniString,
+ defaultValue: true,
+ ref s_useCompatibilityProcessSni);
///
/// In TdsParser, the async multi-packet column value fetch behavior can use a continue snapshot state
@@ -154,18 +138,10 @@ public static bool UseCompatibilityAsyncBehaviour
return true;
}
- if (s_useCompatibilityAsyncBehaviour == SwitchValue.None)
- {
- if (!AppContext.TryGetSwitch(UseCompatibilityAsyncBehaviourString, out bool returnedValue) || returnedValue)
- {
- s_useCompatibilityAsyncBehaviour = SwitchValue.True;
- }
- else
- {
- s_useCompatibilityAsyncBehaviour = SwitchValue.False;
- }
- }
- return s_useCompatibilityAsyncBehaviour == SwitchValue.True;
+ return AcquireAndReturn(
+ UseCompatibilityAsyncBehaviourString,
+ defaultValue: true,
+ ref s_useCompatibilityAsyncBehaviour);
}
}
@@ -174,24 +150,11 @@ public static bool UseCompatibilityAsyncBehaviour
/// This warning can be suppressed by enabling this AppContext switch.
/// This app context switch defaults to 'false'.
///
- public static bool SuppressInsecureTlsWarning
- {
- get
- {
- if (s_suppressInsecureTlsWarning == SwitchValue.None)
- {
- if (AppContext.TryGetSwitch(SuppressInsecureTlsWarningString, out bool returnedValue) && returnedValue)
- {
- s_suppressInsecureTlsWarning = SwitchValue.True;
- }
- else
- {
- s_suppressInsecureTlsWarning = SwitchValue.False;
- }
- }
- return s_suppressInsecureTlsWarning == SwitchValue.True;
- }
- }
+ public static bool SuppressInsecureTlsWarning =>
+ AcquireAndReturn(
+ SuppressInsecureTlsWarningString,
+ defaultValue: false,
+ ref s_suppressInsecureTlsWarning);
///
/// In System.Data.SqlClient and Microsoft.Data.SqlClient prior to 3.0.0 a field with type Timestamp/RowVersion
@@ -199,72 +162,32 @@ public static bool SuppressInsecureTlsWarning
/// of Microsoft.Data.SqlClient, if this switch returns false an appropriate null value will be returned.
/// This app context switch defaults to 'false'.
///
- public static bool LegacyRowVersionNullBehavior
- {
- get
- {
- if (s_legacyRowVersionNullBehavior == SwitchValue.None)
- {
- if (AppContext.TryGetSwitch(LegacyRowVersionNullString, out bool returnedValue) && returnedValue)
- {
- s_legacyRowVersionNullBehavior = SwitchValue.True;
- }
- else
- {
- s_legacyRowVersionNullBehavior = SwitchValue.False;
- }
- }
- return s_legacyRowVersionNullBehavior == SwitchValue.True;
- }
- }
+ public static bool LegacyRowVersionNullBehavior =>
+ AcquireAndReturn(
+ LegacyRowVersionNullString,
+ defaultValue: false,
+ ref s_legacyRowVersionNullBehavior);
///
/// When enabled, ReadAsync runs asynchronously and does not block the calling thread.
/// This app context switch defaults to 'false'.
///
- public static bool MakeReadAsyncBlocking
- {
- get
- {
- if (s_makeReadAsyncBlocking == SwitchValue.None)
- {
- if (AppContext.TryGetSwitch(MakeReadAsyncBlockingString, out bool returnedValue) && returnedValue)
- {
- s_makeReadAsyncBlocking = SwitchValue.True;
- }
- else
- {
- s_makeReadAsyncBlocking = SwitchValue.False;
- }
- }
- return s_makeReadAsyncBlocking == SwitchValue.True;
- }
- }
+ public static bool MakeReadAsyncBlocking =>
+ AcquireAndReturn(
+ MakeReadAsyncBlockingString,
+ defaultValue: false,
+ ref s_makeReadAsyncBlocking);
///
/// Specifies minimum login timeout to be set to 1 second instead of 0 seconds,
/// to prevent a login attempt from waiting indefinitely.
/// This app context switch defaults to 'true'.
///
- public static bool UseMinimumLoginTimeout
- {
- get
- {
- if (s_useMinimumLoginTimeout == SwitchValue.None)
- {
- if (!AppContext.TryGetSwitch(UseMinimumLoginTimeoutString, out bool returnedValue) || returnedValue)
- {
- s_useMinimumLoginTimeout = SwitchValue.True;
- }
- else
- {
- s_useMinimumLoginTimeout = SwitchValue.False;
- }
- }
- return s_useMinimumLoginTimeout == SwitchValue.True;
- }
- }
-
+ public static bool UseMinimumLoginTimeout =>
+ AcquireAndReturn(
+ UseMinimumLoginTimeoutString,
+ defaultValue: true,
+ ref s_useMinimumLoginTimeout);
///
/// When set to 'true' this will output a scale value of 7 (DEFAULT_VARTIME_SCALE) when the scale
@@ -273,70 +196,31 @@ public static bool UseMinimumLoginTimeout
/// regardless of switch value.
/// This app context switch defaults to 'true'.
///
- public static bool LegacyVarTimeZeroScaleBehaviour
- {
- get
- {
- if (s_legacyVarTimeZeroScaleBehaviour == SwitchValue.None)
- {
- if (!AppContext.TryGetSwitch(LegacyVarTimeZeroScaleBehaviourString, out bool returnedValue))
- {
- s_legacyVarTimeZeroScaleBehaviour = SwitchValue.True;
- }
- else
- {
- s_legacyVarTimeZeroScaleBehaviour = returnedValue ? SwitchValue.True : SwitchValue.False;
- }
- }
- return s_legacyVarTimeZeroScaleBehaviour == SwitchValue.True;
- }
- }
+ public static bool LegacyVarTimeZeroScaleBehaviour =>
+ AcquireAndReturn(
+ LegacyVarTimeZeroScaleBehaviourString,
+ defaultValue: true,
+ ref s_legacyVarTimeZeroScaleBehaviour);
///
/// When set to true, the connection pool will use the new V2 connection pool implementation.
/// When set to false, the connection pool will use the legacy V1 implementation.
/// This app context switch defaults to 'false'.
///
- public static bool UseConnectionPoolV2
- {
- get
- {
- if (s_useConnectionPoolV2 == SwitchValue.None)
- {
- if (AppContext.TryGetSwitch(UseConnectionPoolV2String, out bool returnedValue) && returnedValue)
- {
- s_useConnectionPoolV2 = SwitchValue.True;
- }
- else
- {
- s_useConnectionPoolV2 = SwitchValue.False;
- }
- }
- return s_useConnectionPoolV2 == SwitchValue.True;
- }
- }
+ public static bool UseConnectionPoolV2 =>
+ AcquireAndReturn(
+ UseConnectionPoolV2String,
+ defaultValue: false,
+ ref s_useConnectionPoolV2);
///
/// When set to true, TdsParser will truncate (rather than round) decimal and SqlDecimal values when scaling them.
///
- public static bool TruncateScaledDecimal
- {
- get
- {
- if (s_truncateScaledDecimal == SwitchValue.None)
- {
- if (AppContext.TryGetSwitch(TruncateScaledDecimalString, out bool returnedValue) && returnedValue)
- {
- s_truncateScaledDecimal = SwitchValue.True;
- }
- else
- {
- s_truncateScaledDecimal = SwitchValue.False;
- }
- }
- return s_truncateScaledDecimal == SwitchValue.True;
- }
- }
+ public static bool TruncateScaledDecimal =>
+ AcquireAndReturn(
+ TruncateScaledDecimalString,
+ defaultValue: false,
+ ref s_truncateScaledDecimal);
///
/// When set to true, the failover partner provided by the server during connection
@@ -347,45 +231,20 @@ public static bool TruncateScaledDecimal
///
/// This app context switch defaults to 'false'.
///
- public static bool IgnoreServerProvidedFailoverPartner
- {
- get
- {
- if (s_ignoreServerProvidedFailoverPartner == SwitchValue.None)
- {
- if (AppContext.TryGetSwitch(IgnoreServerProvidedFailoverPartnerString, out bool returnedValue) && returnedValue)
- {
- s_ignoreServerProvidedFailoverPartner = SwitchValue.True;
- }
- else
- {
- s_ignoreServerProvidedFailoverPartner = SwitchValue.False;
- }
- }
- return s_ignoreServerProvidedFailoverPartner == SwitchValue.True;
- }
- }
+ public static bool IgnoreServerProvidedFailoverPartner =>
+ AcquireAndReturn(
+ IgnoreServerProvidedFailoverPartnerString,
+ defaultValue: false,
+ ref s_ignoreServerProvidedFailoverPartner);
+
///
/// When set to true, the user agent feature is enabled and the driver will send the user agent string to the server.
///
- public static bool EnableUserAgent
- {
- get
- {
- if (s_enableUserAgent == SwitchValue.None)
- {
- if (AppContext.TryGetSwitch(EnableUserAgentString, out bool returnedValue) && returnedValue)
- {
- s_enableUserAgent = SwitchValue.True;
- }
- else
- {
- s_enableUserAgent = SwitchValue.False;
- }
- }
- return s_enableUserAgent == SwitchValue.True;
- }
- }
+ public static bool EnableUserAgent =>
+ AcquireAndReturn(
+ EnableUserAgentString,
+ defaultValue: false,
+ ref s_enableUserAgent);
#if NET
///
@@ -406,6 +265,17 @@ public static bool GlobalizationInvariantMode
}
else
{
+ // TODO(https://github.com/dotnet/SqlClient/pull/3853):
+ //
+ // The comment's intention doesn't match the code.
+ //
+ // The comment claims to fallback to the environment
+ // variable if the switch is not set. However, it actually
+ // falls-back if the switch is not set _OR_ it is set to
+ // false.
+ //
+ // Should we update the comment or fix the code to match?
+
// If the switch is not set, we check the environment variable as the first fallback
string? envValue = Environment.GetEnvironmentVariable(GlobalizationInvariantModeEnvironmentVariable);
@@ -440,10 +310,7 @@ public static bool GlobalizationInvariantMode
///
/// .NET Framework does not support Globalization Invariant mode, so this will always be false.
///
- public static bool GlobalizationInvariantMode
- {
- get => false;
- }
+ public static bool GlobalizationInvariantMode => false;
#endif
#if NET
@@ -467,22 +334,25 @@ public static bool UseManagedNetworking
{
get
{
- if (s_useManagedNetworking == SwitchValue.None)
+ if (s_useManagedNetworking != SwitchValue.None)
{
- if (!OperatingSystem.IsWindows())
- {
- s_useManagedNetworking = SwitchValue.True;
- }
- else if (AppContext.TryGetSwitch(UseManagedNetworkingOnWindowsString, out bool returnedValue) && returnedValue)
- {
- s_useManagedNetworking = SwitchValue.True;
- }
- else
- {
- s_useManagedNetworking = SwitchValue.False;
- }
+ return s_useManagedNetworking == SwitchValue.True;
+ }
+
+ if (!OperatingSystem.IsWindows())
+ {
+ s_useManagedNetworking = SwitchValue.True;
+ return true;
+ }
+
+ if (AppContext.TryGetSwitch(UseManagedNetworkingOnWindowsString, out bool returnedValue) && returnedValue)
+ {
+ s_useManagedNetworking = SwitchValue.True;
+ return true;
}
- return s_useManagedNetworking == SwitchValue.True;
+
+ s_useManagedNetworking = SwitchValue.False;
+ return false;
}
}
#else
@@ -515,25 +385,12 @@ public static bool UseManagedNetworking
///
/// This app context switch defaults to 'false'.
///
- public static bool DisableTnirByDefault
- {
- get
- {
- if (s_disableTnirByDefault == SwitchValue.None)
- {
- if (AppContext.TryGetSwitch(DisableTnirByDefaultString, out bool returnedValue) && returnedValue)
- {
- s_disableTnirByDefault = SwitchValue.True;
- }
- else
- {
- s_disableTnirByDefault = SwitchValue.False;
- }
- }
- return s_disableTnirByDefault == SwitchValue.True;
- }
- }
-#endif
+ public static bool DisableTnirByDefault =>
+ AcquireAndReturn(
+ DisableTnirByDefaultString,
+ defaultValue: false,
+ ref s_disableTnirByDefault);
+ #endif
///
/// When set to true, the default value for MultiSubnetFailover connection string property
@@ -541,24 +398,56 @@ public static bool DisableTnirByDefault
/// improved connection times in multi-subnet environments.
/// This app context switch defaults to 'false'.
///
- public static bool EnableMultiSubnetFailoverByDefault
+ public static bool EnableMultiSubnetFailoverByDefault =>
+ AcquireAndReturn(
+ EnableMultiSubnetFailoverByDefaultString,
+ defaultValue: false,
+ ref s_multiSubnetFailoverByDefault);
+
+ #endregion
+
+ #region Helpers
+
+ ///
+ /// Acquires the value of the specified app context switch and stores it
+ /// in the given reference. Applies the default value if the switch isn't
+ /// set.
+ ///
+ /// If the cached value is already set, it is returned immediately without
+ /// attempting to re-acquire it.
+ ///
+ /// No attempt is made to prevent multiple threads from acquiring the same
+ /// switch value simultaneously. The worst that can happen is that the
+ /// switch is acquired more than once, and the last acquired value wins.
+ ///
+ /// The name of the app context switch.
+ /// The default value to use if the switch is not set.
+ /// A reference to variable to store the switch value in.
+ /// Returns the acquired value as a bool.
+ private static bool AcquireAndReturn(
+ string switchName,
+ bool defaultValue,
+ ref SwitchValue switchValue)
{
- get
+ // Refuse to re-acquire a switch value. Simply return whatever value
+ // was previously acquired.
+ if (switchValue != SwitchValue.None)
{
- if (s_multiSubnetFailoverByDefault == SwitchValue.None)
- {
- if (AppContext.TryGetSwitch(EnableMultiSubnetFailoverByDefaultString, out bool returnedValue) && returnedValue)
- {
- s_multiSubnetFailoverByDefault = SwitchValue.True;
- }
- else
- {
- s_multiSubnetFailoverByDefault = SwitchValue.False;
- }
- }
- return s_multiSubnetFailoverByDefault == SwitchValue.True;
+ return switchValue == SwitchValue.True;
}
- }
+ // Attempt to acquire the switch value from AppContext.
+ if (! AppContext.TryGetSwitch(switchName, out bool acquiredValue))
+ {
+ // The switch has no value, so use the given default.
+ switchValue = defaultValue ? SwitchValue.True : SwitchValue.False;
+ return defaultValue;
+ }
+
+ // Assign the appropriate SwitchValue based on the acquired value.
+ switchValue = acquiredValue ? SwitchValue.True : SwitchValue.False;
+ return acquiredValue;
+ }
+
#endregion
}
diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/TdsParserStateObject.TestHarness.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/TdsParserStateObject.TestHarness.cs
index 377be3e511..4e70eaae57 100644
--- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/TdsParserStateObject.TestHarness.cs
+++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/TdsParserStateObject.TestHarness.cs
@@ -163,13 +163,13 @@ private void AssertValidState() { }
private class SwitchesHelper : IDisposable
{
- private LocalAppContextSwitchesHelper _helper = new();
+ private readonly LocalAppContextSwitchesHelper _helper = new();
public void Dispose()
{
_helper.Dispose();
}
-
+
public bool UseCompatibilityProcessSni
{
get
@@ -179,7 +179,7 @@ public bool UseCompatibilityProcessSni
}
}
}
- private SwitchesHelper LocalAppContextSwitches = new SwitchesHelper();
+ private readonly SwitchesHelper LocalAppContextSwitches = new SwitchesHelper();
#if NETFRAMEWORK
private SniNativeWrapperImpl _native;
diff --git a/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlConnectionStringTest.cs b/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlConnectionStringTest.cs
index 8c2f879041..844506e92f 100644
--- a/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlConnectionStringTest.cs
+++ b/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlConnectionStringTest.cs
@@ -8,7 +8,7 @@ public class SqlConnectionStringTest : IDisposable
{
// Ensure we restore the original app context switch values after each
// test.
- private LocalAppContextSwitchesHelper _appContextSwitchHelper = new();
+ private readonly LocalAppContextSwitchesHelper _appContextSwitchHelper = new();
public void Dispose()
{
From 6db4226b146c55b45943b5b6ea5ebcd34b014cfc Mon Sep 17 00:00:00 2001
From: Paul Medynski <31868385+paulmedynski@users.noreply.github.com>
Date: Thu, 18 Dec 2025 16:32:22 -0400
Subject: [PATCH 6/7] - Sorted all switch names alphabetically.
---
.../Data/SqlClient/LocalAppContextSwitches.cs | 382 +++++++++---------
.../Common/LocalAppContextSwitchesHelper.cs | 274 +++++++------
2 files changed, 325 insertions(+), 331 deletions(-)
diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/LocalAppContextSwitches.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/LocalAppContextSwitches.cs
index 35fd0981c5..da88c225be 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/LocalAppContextSwitches.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/LocalAppContextSwitches.cs
@@ -12,30 +12,29 @@ internal static class LocalAppContextSwitches
{
#region Switch Names
- private const string MakeReadAsyncBlockingString = @"Switch.Microsoft.Data.SqlClient.MakeReadAsyncBlocking";
- private const string LegacyRowVersionNullString = @"Switch.Microsoft.Data.SqlClient.LegacyRowVersionNullBehavior";
- private const string SuppressInsecureTlsWarningString = @"Switch.Microsoft.Data.SqlClient.SuppressInsecureTLSWarning";
- private const string UseMinimumLoginTimeoutString = @"Switch.Microsoft.Data.SqlClient.UseOneSecFloorInTimeoutCalculationDuringLogin";
- private const string LegacyVarTimeZeroScaleBehaviourString = @"Switch.Microsoft.Data.SqlClient.LegacyVarTimeZeroScaleBehaviour";
- private const string UseCompatibilityProcessSniString = @"Switch.Microsoft.Data.SqlClient.UseCompatibilityProcessSni";
- private const string UseCompatibilityAsyncBehaviourString = @"Switch.Microsoft.Data.SqlClient.UseCompatibilityAsyncBehaviour";
- private const string UseConnectionPoolV2String = @"Switch.Microsoft.Data.SqlClient.UseConnectionPoolV2";
- private const string TruncateScaledDecimalString = @"Switch.Microsoft.Data.SqlClient.TruncateScaledDecimal";
- private const string IgnoreServerProvidedFailoverPartnerString = @"Switch.Microsoft.Data.SqlClient.IgnoreServerProvidedFailoverPartner";
- private const string EnableUserAgentString = @"Switch.Microsoft.Data.SqlClient.EnableUserAgent";
+ #if NETFRAMEWORK
+ private const string DisableTnirByDefaultString = @"Switch.Microsoft.Data.SqlClient.DisableTNIRByDefaultInConnectionString";
+ #endif
private const string EnableMultiSubnetFailoverByDefaultString = @"Switch.Microsoft.Data.SqlClient.EnableMultiSubnetFailoverByDefault";
-
+ private const string EnableUserAgentString = @"Switch.Microsoft.Data.SqlClient.EnableUserAgent";
#if NET
private const string GlobalizationInvariantModeString = @"System.Globalization.Invariant";
private const string GlobalizationInvariantModeEnvironmentVariable = "DOTNET_SYSTEM_GLOBALIZATION_INVARIANT";
-
- #if _WINDOWS
- private const string UseManagedNetworkingOnWindowsString = "Switch.Microsoft.Data.SqlClient.UseManagedNetworkingOnWindows";
#endif
- #else
- private const string DisableTnirByDefaultString = @"Switch.Microsoft.Data.SqlClient.DisableTNIRByDefaultInConnectionString";
+ private const string IgnoreServerProvidedFailoverPartnerString = @"Switch.Microsoft.Data.SqlClient.IgnoreServerProvidedFailoverPartner";
+ private const string LegacyRowVersionNullString = @"Switch.Microsoft.Data.SqlClient.LegacyRowVersionNullBehavior";
+ private const string LegacyVarTimeZeroScaleBehaviourString = @"Switch.Microsoft.Data.SqlClient.LegacyVarTimeZeroScaleBehaviour";
+ private const string MakeReadAsyncBlockingString = @"Switch.Microsoft.Data.SqlClient.MakeReadAsyncBlocking";
+ private const string SuppressInsecureTlsWarningString = @"Switch.Microsoft.Data.SqlClient.SuppressInsecureTLSWarning";
+ private const string TruncateScaledDecimalString = @"Switch.Microsoft.Data.SqlClient.TruncateScaledDecimal";
+ private const string UseCompatibilityAsyncBehaviourString = @"Switch.Microsoft.Data.SqlClient.UseCompatibilityAsyncBehaviour";
+ private const string UseCompatibilityProcessSniString = @"Switch.Microsoft.Data.SqlClient.UseCompatibilityProcessSni";
+ private const string UseConnectionPoolV2String = @"Switch.Microsoft.Data.SqlClient.UseConnectionPoolV2";
+ #if NET && _WINDOWS
+ private const string UseManagedNetworkingOnWindowsString = "Switch.Microsoft.Data.SqlClient.UseManagedNetworkingOnWindows";
#endif
-
+ private const string UseMinimumLoginTimeoutString = @"Switch.Microsoft.Data.SqlClient.UseOneSecFloorInTimeoutCalculationDuringLogin";
+
#endregion
#region Switch Values
@@ -53,28 +52,31 @@ internal enum SwitchValue : byte
False = 2
}
- internal static SwitchValue s_legacyRowVersionNullBehavior = SwitchValue.None;
- internal static SwitchValue s_suppressInsecureTlsWarning = SwitchValue.None;
- internal static SwitchValue s_makeReadAsyncBlocking = SwitchValue.None;
- internal static SwitchValue s_useMinimumLoginTimeout = SwitchValue.None;
- internal static SwitchValue s_legacyVarTimeZeroScaleBehaviour = SwitchValue.None;
- internal static SwitchValue s_useCompatibilityProcessSni = SwitchValue.None;
- internal static SwitchValue s_useCompatibilityAsyncBehaviour = SwitchValue.None;
- internal static SwitchValue s_useConnectionPoolV2 = SwitchValue.None;
- internal static SwitchValue s_truncateScaledDecimal = SwitchValue.None;
- internal static SwitchValue s_ignoreServerProvidedFailoverPartner = SwitchValue.None;
+ // GOTCHA: These fields are accessed via reflection by the
+ // LocalAppContextSwitchesHelper test helper class. If you rename them, be
+ // sure to update the test helper as well.
+
+ #if NETFRAMEWORK
+ internal static SwitchValue s_disableTnirByDefault = SwitchValue.None;
+ #endif
+ internal static SwitchValue s_enableMultiSubnetFailoverByDefault = SwitchValue.None;
internal static SwitchValue s_enableUserAgent = SwitchValue.None;
- internal static SwitchValue s_multiSubnetFailoverByDefault = SwitchValue.None;
-
#if NET
internal static SwitchValue s_globalizationInvariantMode = SwitchValue.None;
#endif
+ internal static SwitchValue s_ignoreServerProvidedFailoverPartner = SwitchValue.None;
+ internal static SwitchValue s_legacyRowVersionNullBehavior = SwitchValue.None;
+ internal static SwitchValue s_legacyVarTimeZeroScaleBehaviour = SwitchValue.None;
+ internal static SwitchValue s_makeReadAsyncBlocking = SwitchValue.None;
+ internal static SwitchValue s_suppressInsecureTlsWarning = SwitchValue.None;
+ internal static SwitchValue s_truncateScaledDecimal = SwitchValue.None;
+ internal static SwitchValue s_useCompatibilityAsyncBehaviour = SwitchValue.None;
+ internal static SwitchValue s_useCompatibilityProcessSni = SwitchValue.None;
+ internal static SwitchValue s_useConnectionPoolV2 = SwitchValue.None;
#if NET && _WINDOWS
- internal static SwitchValue s_useManagedNetworking; = SwitchValue.None;
- #endif
- #if NETFRAMEWORK
- internal static SwitchValue s_disableTnirByDefault = SwitchValue.None;
+ internal static SwitchValue s_useManagedNetworking = SwitchValue.None;
#endif
+ internal static SwitchValue s_useMinimumLoginTimeout = SwitchValue.None;
#endregion
@@ -102,140 +104,40 @@ static LocalAppContextSwitches()
#region Switch Properties
- // @TODO: Sort by name
-
- ///
- /// In TdsParser, the ProcessSni function changed significantly when the packet
- /// multiplexing code needed for high speed multi-packet column values was added.
- /// When this switch is set to true (the default), the old ProcessSni design is used.
- /// When this switch is set to false, the new experimental ProcessSni behavior using
- /// the packet multiplexer is enabled.
- ///
- public static bool UseCompatibilityProcessSni =>
- AcquireAndReturn(
- UseCompatibilityProcessSniString,
- defaultValue: true,
- ref s_useCompatibilityProcessSni);
-
- ///
- /// In TdsParser, the async multi-packet column value fetch behavior can use a continue snapshot state
- /// for improved efficiency. When this switch is enabled (the default), the driver preserves the legacy
- /// compatibility behavior, which does not use the continue snapshot state. When disabled, the new behavior
- /// using the continue snapshot state is enabled. This switch will always return true if
- /// is enabled, because the continue state is not stable without
- /// the multiplexer.
- ///
- public static bool UseCompatibilityAsyncBehaviour
- {
- get
- {
- if (UseCompatibilityProcessSni)
- {
- // If ProcessSni compatibility mode has been enabled then the packet
- // multiplexer has been disabled. The new async behaviour using continue
- // point capture is only stable if the multiplexer is enabled so we must
- // return true to enable compatibility async behaviour using only restarts.
- return true;
- }
-
- return AcquireAndReturn(
- UseCompatibilityAsyncBehaviourString,
- defaultValue: true,
- ref s_useCompatibilityAsyncBehaviour);
- }
- }
-
- ///
- /// When using Encrypt=false in the connection string, a security warning is output to the console if the TLS version is 1.2 or lower.
- /// This warning can be suppressed by enabling this AppContext switch.
- /// This app context switch defaults to 'false'.
- ///
- public static bool SuppressInsecureTlsWarning =>
- AcquireAndReturn(
- SuppressInsecureTlsWarningString,
- defaultValue: false,
- ref s_suppressInsecureTlsWarning);
-
- ///
- /// In System.Data.SqlClient and Microsoft.Data.SqlClient prior to 3.0.0 a field with type Timestamp/RowVersion
- /// would return an empty byte array. This switch controls whether to preserve that behaviour on newer versions
- /// of Microsoft.Data.SqlClient, if this switch returns false an appropriate null value will be returned.
- /// This app context switch defaults to 'false'.
- ///
- public static bool LegacyRowVersionNullBehavior =>
- AcquireAndReturn(
- LegacyRowVersionNullString,
- defaultValue: false,
- ref s_legacyRowVersionNullBehavior);
-
- ///
- /// When enabled, ReadAsync runs asynchronously and does not block the calling thread.
- /// This app context switch defaults to 'false'.
- ///
- public static bool MakeReadAsyncBlocking =>
- AcquireAndReturn(
- MakeReadAsyncBlockingString,
- defaultValue: false,
- ref s_makeReadAsyncBlocking);
-
- ///
- /// Specifies minimum login timeout to be set to 1 second instead of 0 seconds,
- /// to prevent a login attempt from waiting indefinitely.
- /// This app context switch defaults to 'true'.
- ///
- public static bool UseMinimumLoginTimeout =>
- AcquireAndReturn(
- UseMinimumLoginTimeoutString,
- defaultValue: true,
- ref s_useMinimumLoginTimeout);
-
- ///
- /// When set to 'true' this will output a scale value of 7 (DEFAULT_VARTIME_SCALE) when the scale
- /// is explicitly set to zero for VarTime data types ('datetime2', 'datetimeoffset' and 'time')
- /// If no scale is set explicitly it will continue to output scale of 7 (DEFAULT_VARTIME_SCALE)
- /// regardless of switch value.
- /// This app context switch defaults to 'true'.
- ///
- public static bool LegacyVarTimeZeroScaleBehaviour =>
- AcquireAndReturn(
- LegacyVarTimeZeroScaleBehaviourString,
- defaultValue: true,
- ref s_legacyVarTimeZeroScaleBehaviour);
-
+ #if NETFRAMEWORK
///
- /// When set to true, the connection pool will use the new V2 connection pool implementation.
- /// When set to false, the connection pool will use the legacy V1 implementation.
+ /// Transparent Network IP Resolution (TNIR) is a revision of the existing MultiSubnetFailover feature.
+ /// TNIR affects the connection sequence of the driver in the case where the first resolved IP of the hostname
+ /// doesn't respond and there are multiple IPs associated with the hostname.
+ ///
+ /// TNIR interacts with MultiSubnetFailover to provide the following three connection sequences:
+ /// 0: One IP is attempted, followed by all IPs in parallel
+ /// 1: All IPs are attempted in parallel
+ /// 2: All IPs are attempted one after another
+ ///
+ /// TransparentNetworkIPResolution is enabled by default. MultiSubnetFailover is disabled by default.
+ /// To disable TNIR, you can enable the app context switch.
+ ///
/// This app context switch defaults to 'false'.
///
- public static bool UseConnectionPoolV2 =>
- AcquireAndReturn(
- UseConnectionPoolV2String,
- defaultValue: false,
- ref s_useConnectionPoolV2);
-
- ///
- /// When set to true, TdsParser will truncate (rather than round) decimal and SqlDecimal values when scaling them.
- ///
- public static bool TruncateScaledDecimal =>
+ public static bool DisableTnirByDefault =>
AcquireAndReturn(
- TruncateScaledDecimalString,
+ DisableTnirByDefaultString,
defaultValue: false,
- ref s_truncateScaledDecimal);
+ ref s_disableTnirByDefault);
+ #endif
///
- /// When set to true, the failover partner provided by the server during connection
- /// will be ignored. This is useful in scenarios where the application wants to
- /// control the failover behavior explicitly (e.g. using a custom port). The application
- /// must be kept up to date with the failover configuration of the server.
- /// The application will not automatically discover a newly configured failover partner.
- ///
+ /// When set to true, the default value for MultiSubnetFailover connection string property
+ /// will be true instead of false. This enables parallel IP connection attempts for
+ /// improved connection times in multi-subnet environments.
/// This app context switch defaults to 'false'.
///
- public static bool IgnoreServerProvidedFailoverPartner =>
+ public static bool EnableMultiSubnetFailoverByDefault =>
AcquireAndReturn(
- IgnoreServerProvidedFailoverPartnerString,
+ EnableMultiSubnetFailoverByDefaultString,
defaultValue: false,
- ref s_ignoreServerProvidedFailoverPartner);
+ ref s_enableMultiSubnetFailoverByDefault);
///
/// When set to true, the user agent feature is enabled and the driver will send the user agent string to the server.
@@ -313,9 +215,129 @@ public static bool GlobalizationInvariantMode
public static bool GlobalizationInvariantMode => false;
#endif
- #if NET
+ ///
+ /// When set to true, the failover partner provided by the server during connection
+ /// will be ignored. This is useful in scenarios where the application wants to
+ /// control the failover behavior explicitly (e.g. using a custom port). The application
+ /// must be kept up to date with the failover configuration of the server.
+ /// The application will not automatically discover a newly configured failover partner.
+ ///
+ /// This app context switch defaults to 'false'.
+ ///
+ public static bool IgnoreServerProvidedFailoverPartner =>
+ AcquireAndReturn(
+ IgnoreServerProvidedFailoverPartnerString,
+ defaultValue: false,
+ ref s_ignoreServerProvidedFailoverPartner);
+
+ ///
+ /// In System.Data.SqlClient and Microsoft.Data.SqlClient prior to 3.0.0 a field with type Timestamp/RowVersion
+ /// would return an empty byte array. This switch controls whether to preserve that behaviour on newer versions
+ /// of Microsoft.Data.SqlClient, if this switch returns false an appropriate null value will be returned.
+ /// This app context switch defaults to 'false'.
+ ///
+ public static bool LegacyRowVersionNullBehavior =>
+ AcquireAndReturn(
+ LegacyRowVersionNullString,
+ defaultValue: false,
+ ref s_legacyRowVersionNullBehavior);
+
+ ///
+ /// When set to 'true' this will output a scale value of 7 (DEFAULT_VARTIME_SCALE) when the scale
+ /// is explicitly set to zero for VarTime data types ('datetime2', 'datetimeoffset' and 'time')
+ /// If no scale is set explicitly it will continue to output scale of 7 (DEFAULT_VARTIME_SCALE)
+ /// regardless of switch value.
+ /// This app context switch defaults to 'true'.
+ ///
+ public static bool LegacyVarTimeZeroScaleBehaviour =>
+ AcquireAndReturn(
+ LegacyVarTimeZeroScaleBehaviourString,
+ defaultValue: true,
+ ref s_legacyVarTimeZeroScaleBehaviour);
+
+ ///
+ /// When enabled, ReadAsync runs asynchronously and does not block the calling thread.
+ /// This app context switch defaults to 'false'.
+ ///
+ public static bool MakeReadAsyncBlocking =>
+ AcquireAndReturn(
+ MakeReadAsyncBlockingString,
+ defaultValue: false,
+ ref s_makeReadAsyncBlocking);
+
+ ///
+ /// When using Encrypt=false in the connection string, a security warning is output to the console if the TLS version is 1.2 or lower.
+ /// This warning can be suppressed by enabling this AppContext switch.
+ /// This app context switch defaults to 'false'.
+ ///
+ public static bool SuppressInsecureTlsWarning =>
+ AcquireAndReturn(
+ SuppressInsecureTlsWarningString,
+ defaultValue: false,
+ ref s_suppressInsecureTlsWarning);
+
+ ///
+ /// When set to true, TdsParser will truncate (rather than round) decimal and SqlDecimal values when scaling them.
+ ///
+ public static bool TruncateScaledDecimal =>
+ AcquireAndReturn(
+ TruncateScaledDecimalString,
+ defaultValue: false,
+ ref s_truncateScaledDecimal);
+
+ ///
+ /// In TdsParser, the async multi-packet column value fetch behavior can use a continue snapshot state
+ /// for improved efficiency. When this switch is enabled (the default), the driver preserves the legacy
+ /// compatibility behavior, which does not use the continue snapshot state. When disabled, the new behavior
+ /// using the continue snapshot state is enabled. This switch will always return true if
+ /// is enabled, because the continue state is not stable without
+ /// the multiplexer.
+ ///
+ public static bool UseCompatibilityAsyncBehaviour
+ {
+ get
+ {
+ if (UseCompatibilityProcessSni)
+ {
+ // If ProcessSni compatibility mode has been enabled then the packet
+ // multiplexer has been disabled. The new async behaviour using continue
+ // point capture is only stable if the multiplexer is enabled so we must
+ // return true to enable compatibility async behaviour using only restarts.
+ return true;
+ }
+
+ return AcquireAndReturn(
+ UseCompatibilityAsyncBehaviourString,
+ defaultValue: true,
+ ref s_useCompatibilityAsyncBehaviour);
+ }
+ }
+
+ ///
+ /// In TdsParser, the ProcessSni function changed significantly when the packet
+ /// multiplexing code needed for high speed multi-packet column values was added.
+ /// When this switch is set to true (the default), the old ProcessSni design is used.
+ /// When this switch is set to false, the new experimental ProcessSni behavior using
+ /// the packet multiplexer is enabled.
+ ///
+ public static bool UseCompatibilityProcessSni =>
+ AcquireAndReturn(
+ UseCompatibilityProcessSniString,
+ defaultValue: true,
+ ref s_useCompatibilityProcessSni);
+
+ ///
+ /// When set to true, the connection pool will use the new V2 connection pool implementation.
+ /// When set to false, the connection pool will use the legacy V1 implementation.
+ /// This app context switch defaults to 'false'.
+ ///
+ public static bool UseConnectionPoolV2 =>
+ AcquireAndReturn(
+ UseConnectionPoolV2String,
+ defaultValue: false,
+ ref s_useConnectionPoolV2);
- #if _WINDOWS
+ #if NET && _WINDOWS
///
/// When set to true, .NET Core will use the managed SNI implementation instead of the native SNI implementation.
///
@@ -355,13 +377,11 @@ public static bool UseManagedNetworking
return false;
}
}
- #else
+ #elif NET
///
/// .NET Core on Unix does not support the native SNI, so this will always be true.
///
public static bool UseManagedNetworking => true;
- #endif
-
#else
///
/// .NET Framework does not support the managed SNI, so this will always be false.
@@ -369,40 +389,16 @@ public static bool UseManagedNetworking
public static bool UseManagedNetworking => false;
#endif
- #if NETFRAMEWORK
- ///
- /// Transparent Network IP Resolution (TNIR) is a revision of the existing MultiSubnetFailover feature.
- /// TNIR affects the connection sequence of the driver in the case where the first resolved IP of the hostname
- /// doesn't respond and there are multiple IPs associated with the hostname.
- ///
- /// TNIR interacts with MultiSubnetFailover to provide the following three connection sequences:
- /// 0: One IP is attempted, followed by all IPs in parallel
- /// 1: All IPs are attempted in parallel
- /// 2: All IPs are attempted one after another
- ///
- /// TransparentNetworkIPResolution is enabled by default. MultiSubnetFailover is disabled by default.
- /// To disable TNIR, you can enable the app context switch.
- ///
- /// This app context switch defaults to 'false'.
- ///
- public static bool DisableTnirByDefault =>
- AcquireAndReturn(
- DisableTnirByDefaultString,
- defaultValue: false,
- ref s_disableTnirByDefault);
- #endif
-
///
- /// When set to true, the default value for MultiSubnetFailover connection string property
- /// will be true instead of false. This enables parallel IP connection attempts for
- /// improved connection times in multi-subnet environments.
- /// This app context switch defaults to 'false'.
+ /// Specifies minimum login timeout to be set to 1 second instead of 0 seconds,
+ /// to prevent a login attempt from waiting indefinitely.
+ /// This app context switch defaults to 'true'.
///
- public static bool EnableMultiSubnetFailoverByDefault =>
+ public static bool UseMinimumLoginTimeout =>
AcquireAndReturn(
- EnableMultiSubnetFailoverByDefaultString,
- defaultValue: false,
- ref s_multiSubnetFailoverByDefault);
+ UseMinimumLoginTimeoutString,
+ defaultValue: true,
+ ref s_useMinimumLoginTimeout);
#endregion
diff --git a/src/Microsoft.Data.SqlClient/tests/Common/LocalAppContextSwitchesHelper.cs b/src/Microsoft.Data.SqlClient/tests/Common/LocalAppContextSwitchesHelper.cs
index 4c77c4dd26..507fc53a27 100644
--- a/src/Microsoft.Data.SqlClient/tests/Common/LocalAppContextSwitchesHelper.cs
+++ b/src/Microsoft.Data.SqlClient/tests/Common/LocalAppContextSwitchesHelper.cs
@@ -35,30 +35,27 @@ public sealed class LocalAppContextSwitchesHelper : IDisposable
///
/// These fields are used to capture the original switch values.
///
- private readonly bool? _legacyRowVersionNullBehaviorOriginal;
- private readonly bool? _suppressInsecureTlsWarningOriginal;
- private readonly bool? _makeReadAsyncBlockingOriginal;
- private readonly bool? _useMinimumLoginTimeoutOriginal;
- private readonly bool? _legacyVarTimeZeroScaleBehaviourOriginal;
- private readonly bool? _useCompatibilityProcessSniOriginal;
- private readonly bool? _useCompatibilityAsyncBehaviourOriginal;
- private readonly bool? _useConnectionPoolV2Original;
- private readonly bool? _truncateScaledDecimalOriginal;
- private readonly bool? _ignoreServerProvidedFailoverPartnerOriginal;
+ #if NETFRAMEWORK
+ private readonly bool? _disableTnirByDefaultOriginal;
+ #endif
+ private readonly bool? _enableMultiSubnetFailoverByDefaultOriginal;
private readonly bool? _enableUserAgentOriginal;
- private readonly bool? _multiSubnetFailoverByDefaultOriginal;
-
#if NET
private readonly bool? _globalizationInvariantModeOriginal;
#endif
-
+ private readonly bool? _ignoreServerProvidedFailoverPartnerOriginal;
+ private readonly bool? _legacyRowVersionNullBehaviorOriginal;
+ private readonly bool? _legacyVarTimeZeroScaleBehaviourOriginal;
+ private readonly bool? _makeReadAsyncBlockingOriginal;
+ private readonly bool? _suppressInsecureTlsWarningOriginal;
+ private readonly bool? _truncateScaledDecimalOriginal;
+ private readonly bool? _useCompatibilityAsyncBehaviourOriginal;
+ private readonly bool? _useCompatibilityProcessSniOriginal;
+ private readonly bool? _useConnectionPoolV2Original;
#if NET && _WINDOWS
private readonly bool? _useManagedNetworkingOriginal;
#endif
-
- #if NETFRAMEWORK
- private readonly bool? _disableTnirByDefaultOriginal;
- #endif
+ private readonly bool? _useMinimumLoginTimeoutOriginal;
#endregion
@@ -85,42 +82,42 @@ public LocalAppContextSwitchesHelper()
try
{
+ #if NETFRAMEWORK
+ _disableTnirByDefaultOriginal =
+ GetSwitchValue("s_disableTnirByDefault");
+ #endif
+ _enableMultiSubnetFailoverByDefaultOriginal =
+ GetSwitchValue("s_enableMultiSubnetFailoverByDefault");
+ _enableUserAgentOriginal =
+ GetSwitchValue("s_enableUserAgent");
+ #if NET
+ _globalizationInvariantModeOriginal =
+ GetSwitchValue("s_globalizationInvariantMode");
+ #endif
+ _ignoreServerProvidedFailoverPartnerOriginal =
+ GetSwitchValue("s_ignoreServerProvidedFailoverPartner");
_legacyRowVersionNullBehaviorOriginal =
GetSwitchValue("s_legacyRowVersionNullBehavior");
- _suppressInsecureTlsWarningOriginal =
- GetSwitchValue("s_suppressInsecureTlsWarning");
- _makeReadAsyncBlockingOriginal =
- GetSwitchValue("s_makeReadAsyncBlocking");
- _useMinimumLoginTimeoutOriginal =
- GetSwitchValue("s_useMinimumLoginTimeout");
_legacyVarTimeZeroScaleBehaviourOriginal =
GetSwitchValue("s_legacyVarTimeZeroScaleBehaviour");
- _useCompatibilityProcessSniOriginal =
- GetSwitchValue("s_useCompatibilityProcessSni");
+ _makeReadAsyncBlockingOriginal =
+ GetSwitchValue("s_makeReadAsyncBlocking");
+ _suppressInsecureTlsWarningOriginal =
+ GetSwitchValue("s_suppressInsecureTlsWarning");
+ _truncateScaledDecimalOriginal =
+ GetSwitchValue("s_truncateScaledDecimal");
_useCompatibilityAsyncBehaviourOriginal =
GetSwitchValue("s_useCompatibilityAsyncBehaviour");
+ _useCompatibilityProcessSniOriginal =
+ GetSwitchValue("s_useCompatibilityProcessSni");
_useConnectionPoolV2Original =
GetSwitchValue("s_useConnectionPoolV2");
- _truncateScaledDecimalOriginal =
- GetSwitchValue("s_truncateScaledDecimal");
- _ignoreServerProvidedFailoverPartnerOriginal =
- GetSwitchValue("s_ignoreServerProvidedFailoverPartner");
- _enableUserAgentOriginal =
- GetSwitchValue("s_enableUserAgent");
- _multiSubnetFailoverByDefaultOriginal =
- GetSwitchValue("s_multiSubnetFailoverByDefault");
- #if NET
- _globalizationInvariantModeOriginal =
- GetSwitchValue("s_globalizationInvariantMode");
- #endif
#if NET && _WINDOWS
_useManagedNetworkingOriginal =
GetSwitchValue("s_useManagedNetworking");
#endif
- #if NETFRAMEWORK
- _disableTnirByDefaultOriginal =
- GetSwitchValue("s_disableTnirByDefault");
- #endif
+ _useMinimumLoginTimeoutOriginal =
+ GetSwitchValue("s_useMinimumLoginTimeout");
}
catch
{
@@ -138,57 +135,57 @@ public void Dispose()
{
try
{
+ #if NETFRAMEWORK
SetSwitchValue(
- "s_legacyRowVersionNullBehavior",
- _legacyRowVersionNullBehaviorOriginal);
+ "s_disableTnirByDefault",
+ _disableTnirByDefaultOriginal);
+ #endif
SetSwitchValue(
- "s_suppressInsecureTlsWarning",
- _suppressInsecureTlsWarningOriginal);
+ "s_enableMultiSubnetFailoverByDefault",
+ _enableMultiSubnetFailoverByDefaultOriginal);
SetSwitchValue(
- "s_makeReadAsyncBlocking",
- _makeReadAsyncBlockingOriginal);
+ "s_enableUserAgent",
+ _enableUserAgentOriginal);
+ #if NET
SetSwitchValue(
- "s_useMinimumLoginTimeout",
- _useMinimumLoginTimeoutOriginal);
+ "s_globalizationInvariantMode",
+ _globalizationInvariantModeOriginal);
+ #endif
+ SetSwitchValue(
+ "s_ignoreServerProvidedFailoverPartner",
+ _ignoreServerProvidedFailoverPartnerOriginal);
+ SetSwitchValue(
+ "s_legacyRowVersionNullBehavior",
+ _legacyRowVersionNullBehaviorOriginal);
SetSwitchValue(
"s_legacyVarTimeZeroScaleBehaviour",
_legacyVarTimeZeroScaleBehaviourOriginal);
SetSwitchValue(
- "s_useCompatibilityProcessSni",
- _useCompatibilityProcessSniOriginal);
- SetSwitchValue(
- "s_useCompatibilityAsyncBehaviour",
- _useCompatibilityAsyncBehaviourOriginal);
+ "s_makeReadAsyncBlocking",
+ _makeReadAsyncBlockingOriginal);
SetSwitchValue(
- "s_useConnectionPoolV2",
- _useConnectionPoolV2Original);
+ "s_suppressInsecureTlsWarning",
+ _suppressInsecureTlsWarningOriginal);
SetSwitchValue(
"s_truncateScaledDecimal",
_truncateScaledDecimalOriginal);
SetSwitchValue(
- "s_ignoreServerProvidedFailoverPartner",
- _ignoreServerProvidedFailoverPartnerOriginal);
- SetSwitchValue(
- "s_enableUserAgent",
- _enableUserAgentOriginal);
+ "s_useCompatibilityAsyncBehaviour",
+ _useCompatibilityAsyncBehaviourOriginal);
SetSwitchValue(
- "s_multiSubnetFailoverByDefault",
- _multiSubnetFailoverByDefaultOriginal);
- #if NET
+ "s_useCompatibilityProcessSni",
+ _useCompatibilityProcessSniOriginal);
SetSwitchValue(
- "s_globalizationInvariantMode",
- _globalizationInvariantModeOriginal);
- #endif
+ "s_useConnectionPoolV2",
+ _useConnectionPoolV2Original);
#if NET && _WINDOWS
SetSwitchValue(
"s_useManagedNetworking",
_useManagedNetworkingOriginal);
#endif
- #if NETFRAMEWORK
SetSwitchValue(
- "s_disableTnirByDefault",
- _disableTnirByDefaultOriginal);
- #endif
+ "s_useMinimumLoginTimeout",
+ _useMinimumLoginTimeoutOriginal);
}
finally
{
@@ -205,126 +202,130 @@ public void Dispose()
//
// They all throw if the value cannot be retrieved or set.
+ #if NETFRAMEWORK
///
- /// Get or set the LocalAppContextSwitches.LegacyRowVersionNullBehavior
- /// switch value.
+ /// Get or set the DisableTnirByDefault switch value.
///
- public bool? LegacyRowVersionNullBehavior
+ public bool? DisableTnirByDefault
{
- get => GetSwitchValue("s_legacyRowVersionNullBehavior");
- set => SetSwitchValue("s_legacyRowVersionNullBehavior", value);
+ get => GetSwitchValue("s_disableTnirByDefault");
+ set => SetSwitchValue("s_disableTnirByDefault", value);
}
+ #endif
///
- /// Get or set the LocalAppContextSwitches.SuppressInsecureTlsWarning
- /// switch value.
+ /// Get or set the EnableMultiSubnetFailoverByDefault switch value.
///
- public bool? SuppressInsecureTlsWarning
+ public bool? EnableMultiSubnetFailoverByDefault
{
- get => GetSwitchValue("s_suppressInsecureTlsWarning");
- set => SetSwitchValue("s_suppressInsecureTlsWarning", value);
+ get => GetSwitchValue("s_enableMultiSubnetFailoverByDefault");
+ set => SetSwitchValue("s_enableMultiSubnetFailoverByDefault", value);
}
///
- /// Get or set the LocalAppContextSwitches.MakeReadAsyncBlocking switch
- /// value.
+ /// Get or set the EnableUserAgent switch value.
///
- public bool? MakeReadAsyncBlocking
+ public bool? EnableUserAgent
{
- get => GetSwitchValue("s_makeReadAsyncBlocking");
- set => SetSwitchValue("s_makeReadAsyncBlocking", value);
+ get => GetSwitchValue("s_enableUserAgent");
+ set => SetSwitchValue("s_enableUserAgent", value);
}
+ #if NET
///
- /// Get or set the LocalAppContextSwitches.UseMinimumLoginTimeout switch
- /// value.
+ /// Get or set the GlobalizationInvariantMode switch value.
///
- public bool? UseMinimumLoginTimeout
+ public bool? GlobalizationInvariantMode
{
- get => GetSwitchValue("s_useMinimumLoginTimeout");
- set => SetSwitchValue("s_useMinimumLoginTimeout", value);
+ get => GetSwitchValue("s_globalizationInvariantMode");
+ set => SetSwitchValue("s_globalizationInvariantMode", value);
}
+ #endif
///
- /// Get or set the LocalAppContextSwitches.LegacyVarTimeZeroScaleBehaviour
- /// switch value.
+ /// Get or set the IgnoreServerProvidedFailoverPartner switch value.
///
- public bool? LegacyVarTimeZeroScaleBehaviour
+ public bool? IgnoreServerProvidedFailoverPartner
{
- get => GetSwitchValue("s_legacyVarTimeZeroScaleBehaviour");
- set => SetSwitchValue("s_legacyVarTimeZeroScaleBehaviour", value);
+ get => GetSwitchValue("s_ignoreServerProvidedFailoverPartner");
+ set => SetSwitchValue("s_ignoreServerProvidedFailoverPartner", value);
}
///
- /// Get or set the LocalAppContextSwitches.UseCompatibilityProcessSni switch
- /// value.
+ /// Get or set the LegacyRowVersionNullBehavior switch value.
///
- public bool? UseCompatibilityProcessSni
+ public bool? LegacyRowVersionNullBehavior
{
- get => GetSwitchValue("s_useCompatibilityProcessSni");
- set => SetSwitchValue("s_useCompatibilityProcessSni", value);
+ get => GetSwitchValue("s_legacyRowVersionNullBehavior");
+ set => SetSwitchValue("s_legacyRowVersionNullBehavior", value);
}
///
- /// Get or set the LocalAppContextSwitches.UseCompatibilityAsyncBehaviour
- /// switch value.
+ /// Get or set the LegacyVarTimeZeroScaleBehaviour switch value.
///
- public bool? UseCompatibilityAsyncBehaviour
+ public bool? LegacyVarTimeZeroScaleBehaviour
{
- get => GetSwitchValue("s_useCompatibilityAsyncBehaviour");
- set => SetSwitchValue("s_useCompatibilityAsyncBehaviour", value);
+ get => GetSwitchValue("s_legacyVarTimeZeroScaleBehaviour");
+ set => SetSwitchValue("s_legacyVarTimeZeroScaleBehaviour", value);
}
///
- /// Get or set the LocalAppContextSwitches.UseConnectionPoolV2 switch value.
+ /// Get or set the MakeReadAsyncBlocking switch value.
///
- public bool? UseConnectionPoolV2
+ public bool? MakeReadAsyncBlocking
{
- get => GetSwitchValue("s_useConnectionPoolV2");
- set => SetSwitchValue("s_useConnectionPoolV2", value);
+ get => GetSwitchValue("s_makeReadAsyncBlocking");
+ set => SetSwitchValue("s_makeReadAsyncBlocking", value);
}
///
- /// Get or set the LocalAppContextSwitches.TruncateScaledDecimal switch value.
+ /// Get or set the SuppressInsecureTlsWarning switch value.
///
- public bool? TruncateScaledDecimal
+ public bool? SuppressInsecureTlsWarning
{
- get => GetSwitchValue("s_truncateScaledDecimal");
- set => SetSwitchValue("s_truncateScaledDecimal", value);
+ get => GetSwitchValue("s_suppressInsecureTlsWarning");
+ set => SetSwitchValue("s_suppressInsecureTlsWarning", value);
}
- public bool? IgnoreServerProvidedFailoverPartner
+ ///
+ /// Get or set the TruncateScaledDecimal switch value.
+ ///
+ public bool? TruncateScaledDecimal
{
- get => GetSwitchValue("s_ignoreServerProvidedFailoverPartner");
- set => SetSwitchValue("s_ignoreServerProvidedFailoverPartner", value);
+ get => GetSwitchValue("s_truncateScaledDecimal");
+ set => SetSwitchValue("s_truncateScaledDecimal", value);
}
- public bool? EnableUserAgent
+ ///
+ /// Get or set the UseCompatibilityAsyncBehaviour switch value.
+ ///
+ public bool? UseCompatibilityAsyncBehaviour
{
- get => GetSwitchValue("s_enableUserAgent");
- set => SetSwitchValue("s_enableUserAgent", value);
+ get => GetSwitchValue("s_useCompatibilityAsyncBehaviour");
+ set => SetSwitchValue("s_useCompatibilityAsyncBehaviour", value);
}
- public bool? EnableMultiSubnetFailoverByDefault
+ ///
+ /// Get or set the UseCompatibilityProcessSni switch value.
+ ///
+ public bool? UseCompatibilityProcessSni
{
- get => GetSwitchValue("s_multiSubnetFailoverByDefault");
- set => SetSwitchValue("s_multiSubnetFailoverByDefault", value);
+ get => GetSwitchValue("s_useCompatibilityProcessSni");
+ set => SetSwitchValue("s_useCompatibilityProcessSni", value);
}
-#if NET
///
- /// Get or set the LocalAppContextSwitches.GlobalizationInvariantMode switch value.
+ /// Get or set the UseConnectionPoolV2 switch value.
///
- public bool? GlobalizationInvariantMode
+ public bool? UseConnectionPoolV2
{
- get => GetSwitchValue("s_globalizationInvariantMode");
- set => SetSwitchValue("s_globalizationInvariantMode", value);
+ get => GetSwitchValue("s_useConnectionPoolV2");
+ set => SetSwitchValue("s_useConnectionPoolV2", value);
}
- #endif
#if NET && _WINDOWS
///
- /// Get or set the LocalAppContextSwitches.UseManagedNetworking switch value.
+ /// Get or set the UseManagedNetworking switch value.
///
public bool? UseManagedNetworking
{
@@ -332,18 +333,15 @@ public bool? UseManagedNetworking
set => SetSwitchValue("s_useManagedNetworking", value);
}
#endif
-
- #if NETFRAMEWORK
+
///
- /// Get or set the LocalAppContextSwitches.DisableTnirByDefault switch
- /// value.
+ /// Get or set the UseMinimumLoginTimeout switch value.
///
- public bool? DisableTnirByDefault
+ public bool? UseMinimumLoginTimeout
{
- get => GetSwitchValue("s_disableTnirByDefault");
- set => SetSwitchValue("s_disableTnirByDefault", value);
+ get => GetSwitchValue("s_useMinimumLoginTimeout");
+ set => SetSwitchValue("s_useMinimumLoginTimeout", value);
}
- #endif
#endregion
From a50cefceda72c779cf41a61f5fa0951b42c1d7cc Mon Sep 17 00:00:00 2001
From: Paul Medynski <31868385+paulmedynski@users.noreply.github.com>
Date: Mon, 22 Dec 2025 08:24:02 -0400
Subject: [PATCH 7/7] - Added XML comments throughout the
LocalAppContextSwitches class. - Re-formatted existing XML comments for
consistency.
---
.../Data/SqlClient/LocalAppContextSwitches.cs | 545 +++++++++++++-----
.../Common/LocalAppContextSwitchesHelper.cs | 8 +-
2 files changed, 391 insertions(+), 162 deletions(-)
diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/LocalAppContextSwitches.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/LocalAppContextSwitches.cs
index da88c225be..74fd226728 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/LocalAppContextSwitches.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/LocalAppContextSwitches.cs
@@ -8,44 +8,152 @@
namespace Microsoft.Data.SqlClient;
+///
+/// This class provides immutable access to the app context switches used
+/// throughout the codebase. Each switch value is read once (on first access)
+/// and cached for future access. Most switch values are obtained solely from
+/// the AppContext, but some switches may use other mechanisms to determine
+/// their values as well (e.g. environment variables).
+///
internal static class LocalAppContextSwitches
{
#region Switch Names
#if NETFRAMEWORK
- private const string DisableTnirByDefaultString = @"Switch.Microsoft.Data.SqlClient.DisableTNIRByDefaultInConnectionString";
+ ///
+ /// The name of the app context switch that controls whether TNIR is
+ /// disabled by default in the connection string.
+ ///
+ private const string DisableTnirByDefaultString =
+ "Switch.Microsoft.Data.SqlClient.DisableTNIRByDefaultInConnectionString";
#endif
- private const string EnableMultiSubnetFailoverByDefaultString = @"Switch.Microsoft.Data.SqlClient.EnableMultiSubnetFailoverByDefault";
- private const string EnableUserAgentString = @"Switch.Microsoft.Data.SqlClient.EnableUserAgent";
+
+ ///
+ /// The name of the app context switch that controls whether
+ /// MultiSubnetFailover is enabled by default in the connection string.
+ ///
+ private const string EnableMultiSubnetFailoverByDefaultString =
+ "Switch.Microsoft.Data.SqlClient.EnableMultiSubnetFailoverByDefault";
+
+ ///
+ /// The name of the app context switch that controls whether
+ /// the user agent feature is enabled.
+ ///
+ private const string EnableUserAgentString =
+ "Switch.Microsoft.Data.SqlClient.EnableUserAgent";
+
#if NET
- private const string GlobalizationInvariantModeString = @"System.Globalization.Invariant";
- private const string GlobalizationInvariantModeEnvironmentVariable = "DOTNET_SYSTEM_GLOBALIZATION_INVARIANT";
+ ///
+ /// The name of the app context switch that controls whether
+ /// Globalization Invariant mode is enabled.
+ ///
+ private const string GlobalizationInvariantModeString =
+ "System.Globalization.Invariant";
+ ///
+ /// The name of the environment variable that controls whether
+ /// Globalization Invariant mode is enabled.
+ ///
+ private const string GlobalizationInvariantModeEnvironmentVariable =
+ "DOTNET_SYSTEM_GLOBALIZATION_INVARIANT";
#endif
- private const string IgnoreServerProvidedFailoverPartnerString = @"Switch.Microsoft.Data.SqlClient.IgnoreServerProvidedFailoverPartner";
- private const string LegacyRowVersionNullString = @"Switch.Microsoft.Data.SqlClient.LegacyRowVersionNullBehavior";
- private const string LegacyVarTimeZeroScaleBehaviourString = @"Switch.Microsoft.Data.SqlClient.LegacyVarTimeZeroScaleBehaviour";
- private const string MakeReadAsyncBlockingString = @"Switch.Microsoft.Data.SqlClient.MakeReadAsyncBlocking";
- private const string SuppressInsecureTlsWarningString = @"Switch.Microsoft.Data.SqlClient.SuppressInsecureTLSWarning";
- private const string TruncateScaledDecimalString = @"Switch.Microsoft.Data.SqlClient.TruncateScaledDecimal";
- private const string UseCompatibilityAsyncBehaviourString = @"Switch.Microsoft.Data.SqlClient.UseCompatibilityAsyncBehaviour";
- private const string UseCompatibilityProcessSniString = @"Switch.Microsoft.Data.SqlClient.UseCompatibilityProcessSni";
- private const string UseConnectionPoolV2String = @"Switch.Microsoft.Data.SqlClient.UseConnectionPoolV2";
+
+ ///
+ /// The name of the app context switch that controls whether the failover
+ /// partner provided by the server during connection will be ignored.
+ ///
+ private const string IgnoreServerProvidedFailoverPartnerString =
+ "Switch.Microsoft.Data.SqlClient.IgnoreServerProvidedFailoverPartner";
+
+ ///
+ /// The name of the app context switch that controls whether to preserve
+ /// legacy behavior where Timestamp/RowVersion fields return empty byte
+ /// arrays instead of null.
+ ///
+ private const string LegacyRowVersionNullString =
+ "Switch.Microsoft.Data.SqlClient.LegacyRowVersionNullBehavior";
+
+ ///
+ /// The name of the app context switch that controls whether to preserve
+ /// legacy VarTime zero scale behavior for datetime2, datetimeoffset, and
+ /// time data types.
+ ///
+ private const string LegacyVarTimeZeroScaleBehaviourString =
+ "Switch.Microsoft.Data.SqlClient.LegacyVarTimeZeroScaleBehaviour";
+
+ ///
+ /// The name of the app context switch that controls whether
+ /// ReadAsync operations run synchronously and block the calling thread.
+ ///
+ private const string MakeReadAsyncBlockingString =
+ "Switch.Microsoft.Data.SqlClient.MakeReadAsyncBlocking";
+
+ ///
+ /// The name of the app context switch that controls whether to suppress
+ /// the security warning when using Encrypt=false with TLS 1.2 or lower.
+ ///
+ private const string SuppressInsecureTlsWarningString =
+ "Switch.Microsoft.Data.SqlClient.SuppressInsecureTLSWarning";
+
+ ///
+ /// The name of the app context switch that controls whether TdsParser
+ /// truncates (rather than rounds) decimal and SqlDecimal values when
+ /// scaling them.
+ ///
+ private const string TruncateScaledDecimalString =
+ "Switch.Microsoft.Data.SqlClient.TruncateScaledDecimal";
+
+ ///
+ /// The name of the app context switch that controls whether to use legacy
+ /// async multi-packet column value fetch behavior without continue snapshot
+ /// state.
+ ///
+ private const string UseCompatibilityAsyncBehaviourString =
+ "Switch.Microsoft.Data.SqlClient.UseCompatibilityAsyncBehaviour";
+
+ ///
+ /// The name of the app context switch that controls whether to use the old
+ /// ProcessSni design instead of the packet multiplexing implementation.
+ ///
+ private const string UseCompatibilityProcessSniString =
+ "Switch.Microsoft.Data.SqlClient.UseCompatibilityProcessSni";
+
+ ///
+ /// The name of the app context switch that controls whether to use the new
+ /// V2 connection pool implementation or the legacy V1 implementation.
+ ///
+ private const string UseConnectionPoolV2String =
+ "Switch.Microsoft.Data.SqlClient.UseConnectionPoolV2";
+
#if NET && _WINDOWS
- private const string UseManagedNetworkingOnWindowsString = "Switch.Microsoft.Data.SqlClient.UseManagedNetworkingOnWindows";
+ ///
+ /// The name of the app context switch that controls whether to use the
+ /// managed SNI implementation instead of the native SNI implementation on
+ /// Windows.
+ ///
+ private const string UseManagedNetworkingOnWindowsString =
+ "Switch.Microsoft.Data.SqlClient.UseManagedNetworkingOnWindows";
#endif
- private const string UseMinimumLoginTimeoutString = @"Switch.Microsoft.Data.SqlClient.UseOneSecFloorInTimeoutCalculationDuringLogin";
+
+ ///
+ /// The name of the app context switch that controls whether to enforce
+ /// a minimum login timeout of 1 second instead of 0 seconds.
+ ///
+ private const string UseMinimumLoginTimeoutString =
+ "Switch.Microsoft.Data.SqlClient.UseOneSecFloorInTimeoutCalculationDuringLogin";
#endregion
#region Switch Values
- // We use a byte-based enum to track the value of each switch. This plays
- // nicely with threaded access. A nullable bool would seem to be the
- // obvious choice, but the way nullable bools are implemented in the CLR
- // makes them not thread-safe without using locks (the HasValue and Value
- // properties can get out of sync if one thread is writing while another is
- // reading).
- internal enum SwitchValue : byte
+ ///
+ /// We use a byte-based enum to track the value of each switch. This plays
+ /// nicely with threaded access. A nullable bool would seem to be the
+ /// obvious choice, but the way nullable bools are implemented in the CLR
+ /// makes them not thread-safe without using locks (the HasValue and Value
+ /// properties can get out of sync if one thread is writing while another is
+ /// reading).
+ ///
+ private enum SwitchValue : byte
{
None = 0,
True = 1,
@@ -57,32 +165,97 @@ internal enum SwitchValue : byte
// sure to update the test helper as well.
#if NETFRAMEWORK
- internal static SwitchValue s_disableTnirByDefault = SwitchValue.None;
+ ///
+ /// The cached value of the DisableTnirByDefault switch.
+ ///
+ private static SwitchValue s_disableTnirByDefault = SwitchValue.None;
#endif
- internal static SwitchValue s_enableMultiSubnetFailoverByDefault = SwitchValue.None;
- internal static SwitchValue s_enableUserAgent = SwitchValue.None;
+
+ ///
+ /// The cached value of the EnableMultiSubnetFailoverByDefault switch.
+ ///
+ private static SwitchValue s_enableMultiSubnetFailoverByDefault = SwitchValue.None;
+
+ ///
+ /// The cached value of the EnableUserAgent switch.
+ ///
+ private static SwitchValue s_enableUserAgent = SwitchValue.None;
+
#if NET
- internal static SwitchValue s_globalizationInvariantMode = SwitchValue.None;
+ ///
+ /// The cached value of the GlobalizationInvariantMode switch.
+ ///
+ private static SwitchValue s_globalizationInvariantMode = SwitchValue.None;
#endif
- internal static SwitchValue s_ignoreServerProvidedFailoverPartner = SwitchValue.None;
- internal static SwitchValue s_legacyRowVersionNullBehavior = SwitchValue.None;
- internal static SwitchValue s_legacyVarTimeZeroScaleBehaviour = SwitchValue.None;
- internal static SwitchValue s_makeReadAsyncBlocking = SwitchValue.None;
- internal static SwitchValue s_suppressInsecureTlsWarning = SwitchValue.None;
- internal static SwitchValue s_truncateScaledDecimal = SwitchValue.None;
- internal static SwitchValue s_useCompatibilityAsyncBehaviour = SwitchValue.None;
- internal static SwitchValue s_useCompatibilityProcessSni = SwitchValue.None;
- internal static SwitchValue s_useConnectionPoolV2 = SwitchValue.None;
+
+ ///
+ /// The cached value of the IgnoreServerProvidedFailoverPartner switch.
+ ///
+ private static SwitchValue s_ignoreServerProvidedFailoverPartner = SwitchValue.None;
+
+ ///
+ /// The cached value of the LegacyRowVersionNullBehavior switch.
+ ///
+ private static SwitchValue s_legacyRowVersionNullBehavior = SwitchValue.None;
+
+ ///
+ /// The cached value of the LegacyVarTimeZeroScaleBehaviour switch.
+ ///
+ private static SwitchValue s_legacyVarTimeZeroScaleBehaviour = SwitchValue.None;
+
+ ///
+ /// The cached value of the MakeReadAsyncBlocking switch.
+ ///
+ private static SwitchValue s_makeReadAsyncBlocking = SwitchValue.None;
+
+ ///
+ /// The cached value of the SuppressInsecureTlsWarning switch.
+ ///
+ private static SwitchValue s_suppressInsecureTlsWarning = SwitchValue.None;
+
+ ///
+ /// The cached value of the TruncateScaledDecimal switch.
+ ///
+ private static SwitchValue s_truncateScaledDecimal = SwitchValue.None;
+
+ ///
+ /// The cached value of the UseCompatibilityAsyncBehaviour switch.
+ ///
+ private static SwitchValue s_useCompatibilityAsyncBehaviour = SwitchValue.None;
+
+ ///
+ /// The cached value of the UseCompatibilityProcessSni switch.
+ ///
+ private static SwitchValue s_useCompatibilityProcessSni = SwitchValue.None;
+
+ ///
+ /// The cached value of the UseConnectionPoolV2 switch.
+ ///
+ private static SwitchValue s_useConnectionPoolV2 = SwitchValue.None;
+
#if NET && _WINDOWS
- internal static SwitchValue s_useManagedNetworking = SwitchValue.None;
+ ///
+ /// The cached value of the UseManagedNetworking switch.
+ ///
+ private static SwitchValue s_useManagedNetworking = SwitchValue.None;
#endif
- internal static SwitchValue s_useMinimumLoginTimeout = SwitchValue.None;
+
+ ///
+ /// The cached value of the UseMinimumLoginTimeout switch.
+ ///
+ private static SwitchValue s_useMinimumLoginTimeout = SwitchValue.None;
#endregion
#region Static Initialization
#if NET
+ ///
+ /// Static construction for .NET reads the AppContextSwitchOverridesSection
+ /// from the default app config file and applies any switch values found
+ /// there to the AppContext. These values are then later read and cached by
+ /// the individual switch properties on first access.
+ ///
static LocalAppContextSwitches()
{
IAppContextSwitchOverridesSection appContextSwitch = AppConfigManager.FetchConfigurationSection(AppContextSwitchOverridesSection.Name);
@@ -106,19 +279,23 @@ static LocalAppContextSwitches()
#if NETFRAMEWORK
///
- /// Transparent Network IP Resolution (TNIR) is a revision of the existing MultiSubnetFailover feature.
- /// TNIR affects the connection sequence of the driver in the case where the first resolved IP of the hostname
+ /// Transparent Network IP Resolution (TNIR) is a revision of the existing
+ /// MultiSubnetFailover feature. TNIR affects the connection sequence of
+ /// the driver in the case where the first resolved IP of the hostname
/// doesn't respond and there are multiple IPs associated with the hostname.
///
- /// TNIR interacts with MultiSubnetFailover to provide the following three connection sequences:
- /// 0: One IP is attempted, followed by all IPs in parallel
- /// 1: All IPs are attempted in parallel
- /// 2: All IPs are attempted one after another
+ /// TNIR interacts with MultiSubnetFailover to provide the following three
+ /// connection sequences:
///
- /// TransparentNetworkIPResolution is enabled by default. MultiSubnetFailover is disabled by default.
- /// To disable TNIR, you can enable the app context switch.
+ /// 0: One IP is attempted, followed by all IPs in parallel
+ /// 1: All IPs are attempted in parallel
+ /// 2: All IPs are attempted one after another
///
- /// This app context switch defaults to 'false'.
+ /// TransparentNetworkIPResolution is enabled by default.
+ /// MultiSubnetFailover is disabled by default. To disable TNIR, you can
+ /// enable the app context switch.
+ ///
+ /// The default value of this switch is false.
///
public static bool DisableTnirByDefault =>
AcquireAndReturn(
@@ -128,10 +305,12 @@ static LocalAppContextSwitches()
#endif
///
- /// When set to true, the default value for MultiSubnetFailover connection string property
- /// will be true instead of false. This enables parallel IP connection attempts for
- /// improved connection times in multi-subnet environments.
- /// This app context switch defaults to 'false'.
+ /// When set to true, the default value for MultiSubnetFailover connection
+ /// string property will be true instead of false. This enables parallel IP
+ /// connection attempts for improved connection times in multi-subnet
+ /// environments.
+ ///
+ /// The default value of this switch is false.
///
public static bool EnableMultiSubnetFailoverByDefault =>
AcquireAndReturn(
@@ -140,7 +319,10 @@ static LocalAppContextSwitches()
ref s_enableMultiSubnetFailoverByDefault);
///
- /// When set to true, the user agent feature is enabled and the driver will send the user agent string to the server.
+ /// When set to true, the user agent feature is enabled and the driver will
+ /// send the user agent string to the server.
+ ///
+ /// The default value of this switch is false.
///
public static bool EnableUserAgent =>
AcquireAndReturn(
@@ -150,79 +332,99 @@ static LocalAppContextSwitches()
#if NET
///
- /// .NET Core 2.0 and up supports Globalization Invariant mode, which reduces the size of the required libraries for
- /// applications which don't need globalization support. SqlClient requires those libraries for core functionality,
- /// and will throw exceptions later if they are not present. This switch allows SqlClient to detect this mode early.
+ /// .NET Core 2.0 and up supports Globalization Invariant mode, which
+ /// reduces the size of the required libraries for applications which don't
+ /// need globalization support. SqlClient requires those libraries for core
+ /// functionality, and will throw exceptions later if they are not present.
+ /// This switch allows SqlClient to detect this mode early.
+ ///
+ /// The value of this switch is determined first by checking the AppContext.
+ /// If not set there, it falls back to checking the environment variable
+ /// DOTNET_SYSTEM_GLOBALIZATION_INVARIANT. If neither is set, it attempts to
+ /// create the "en-US" culture and infers invariant mode from whether that
+ /// operation throws an exception.
+ ///
+ /// The default value of this switch is false.
///
public static bool GlobalizationInvariantMode
{
get
{
- if (s_globalizationInvariantMode == SwitchValue.None)
+ if (s_globalizationInvariantMode != SwitchValue.None)
+ {
+ return s_globalizationInvariantMode == SwitchValue.True;
+ }
+
+ // Check if invariant mode has been set by the AppContext switch directly
+ if (AppContext.TryGetSwitch(GlobalizationInvariantModeString, out bool returnedValue) && returnedValue)
+ {
+ s_globalizationInvariantMode = SwitchValue.True;
+ return true;
+ }
+
+ // TODO(https://github.com/dotnet/SqlClient/pull/3853):
+ //
+ // The intention of the comment below doesn't match the code.
+ //
+ // The comment claims to fallback to the environment variable if the
+ // switch is not set. However, it actually falls-back if the switch
+ // is not set _OR_ it is set to false.
+ //
+ // Should we update the comment or fix the code to match?
+
+ // If the switch is not set, we check the environment variable as the first fallback
+ string? envValue = Environment.GetEnvironmentVariable(GlobalizationInvariantModeEnvironmentVariable);
+
+ if (string.Equals(envValue, bool.TrueString, StringComparison.OrdinalIgnoreCase) ||
+ string.Equals(envValue, "1", StringComparison.OrdinalIgnoreCase))
+ {
+ s_globalizationInvariantMode = SwitchValue.True;
+ return true;
+ }
+
+ // TODO(https://github.com/dotnet/SqlClient/pull/3853):
+ //
+ // What if the environment variable is set to false? Why are we
+ // ignoring that case?
+
+ // If this hasn't been manually set, it could still apply if the OS
+ // doesn't have ICU libraries installed, or if the application is a
+ // native binary with ICU support trimmed away. .NET 3.1 to 5.0 do
+ // not throw in attempting to create en-US in invariant mode, but
+ // .NET 6+ does. In such cases, catch and infer invariant mode from
+ // the exception.
+ try
+ {
+ s_globalizationInvariantMode = System.Globalization.CultureInfo.GetCultureInfo("en-US").EnglishName.Contains("Invariant")
+ ? SwitchValue.True
+ : SwitchValue.False;
+ }
+ catch (System.Globalization.CultureNotFoundException)
{
- // Check if invariant mode has been set by the AppContext switch directly
- if (AppContext.TryGetSwitch(GlobalizationInvariantModeString, out bool returnedValue) && returnedValue)
- {
- s_globalizationInvariantMode = SwitchValue.True;
- }
- else
- {
- // TODO(https://github.com/dotnet/SqlClient/pull/3853):
- //
- // The comment's intention doesn't match the code.
- //
- // The comment claims to fallback to the environment
- // variable if the switch is not set. However, it actually
- // falls-back if the switch is not set _OR_ it is set to
- // false.
- //
- // Should we update the comment or fix the code to match?
-
- // If the switch is not set, we check the environment variable as the first fallback
- string? envValue = Environment.GetEnvironmentVariable(GlobalizationInvariantModeEnvironmentVariable);
-
- if (string.Equals(envValue, bool.TrueString, StringComparison.OrdinalIgnoreCase) || string.Equals(envValue, "1", StringComparison.OrdinalIgnoreCase))
- {
- s_globalizationInvariantMode = SwitchValue.True;
- }
- else
- {
- // If this hasn't been manually set, it could still apply if the OS doesn't have ICU libraries installed,
- // or if the application is a native binary with ICU support trimmed away.
- // .NET 3.1 to 5.0 do not throw in attempting to create en-US in invariant mode, but .NET 6+ does. In
- // such cases, catch and infer invariant mode from the exception.
- try
- {
- s_globalizationInvariantMode = System.Globalization.CultureInfo.GetCultureInfo("en-US").EnglishName.Contains("Invariant")
- ? SwitchValue.True
- : SwitchValue.False;
- }
- catch (System.Globalization.CultureNotFoundException)
- {
- // If the culture is not found, it means we are in invariant mode
- s_globalizationInvariantMode = SwitchValue.True;
- }
- }
- }
+ // If the culture is not found, it means we are in invariant mode
+ s_globalizationInvariantMode = SwitchValue.True;
}
+
return s_globalizationInvariantMode == SwitchValue.True;
}
}
#else
///
- /// .NET Framework does not support Globalization Invariant mode, so this will always be false.
+ /// .NET Framework does not support Globalization Invariant mode, so this
+ /// will always be false.
///
public static bool GlobalizationInvariantMode => false;
#endif
///
- /// When set to true, the failover partner provided by the server during connection
- /// will be ignored. This is useful in scenarios where the application wants to
- /// control the failover behavior explicitly (e.g. using a custom port). The application
- /// must be kept up to date with the failover configuration of the server.
- /// The application will not automatically discover a newly configured failover partner.
- ///
- /// This app context switch defaults to 'false'.
+ /// When set to true, the failover partner provided by the server during
+ /// connection will be ignored. This is useful in scenarios where the
+ /// application wants to control the failover behavior explicitly (e.g.
+ /// using a custom port). The application must be kept up to date with the
+ /// failover configuration of the server. The application will not
+ /// automatically discover a newly configured failover partner.
+ ///
+ /// The default value of this switch is false.
///
public static bool IgnoreServerProvidedFailoverPartner =>
AcquireAndReturn(
@@ -231,10 +433,13 @@ public static bool GlobalizationInvariantMode
ref s_ignoreServerProvidedFailoverPartner);
///
- /// In System.Data.SqlClient and Microsoft.Data.SqlClient prior to 3.0.0 a field with type Timestamp/RowVersion
- /// would return an empty byte array. This switch controls whether to preserve that behaviour on newer versions
- /// of Microsoft.Data.SqlClient, if this switch returns false an appropriate null value will be returned.
- /// This app context switch defaults to 'false'.
+ /// In System.Data.SqlClient and Microsoft.Data.SqlClient prior to 3.0.0 a
+ /// field with type Timestamp/RowVersion would return an empty byte array.
+ /// This switch controls whether to preserve that behaviour on newer
+ /// versions of Microsoft.Data.SqlClient, if this switch returns false an
+ /// appropriate null value will be returned.
+ ///
+ /// The default value of this switch is false.
///
public static bool LegacyRowVersionNullBehavior =>
AcquireAndReturn(
@@ -243,11 +448,13 @@ public static bool GlobalizationInvariantMode
ref s_legacyRowVersionNullBehavior);
///
- /// When set to 'true' this will output a scale value of 7 (DEFAULT_VARTIME_SCALE) when the scale
- /// is explicitly set to zero for VarTime data types ('datetime2', 'datetimeoffset' and 'time')
- /// If no scale is set explicitly it will continue to output scale of 7 (DEFAULT_VARTIME_SCALE)
- /// regardless of switch value.
- /// This app context switch defaults to 'true'.
+ /// When set to 'true' this will output a scale value of 7
+ /// (DEFAULT_VARTIME_SCALE) when the scale is explicitly set to zero for
+ /// VarTime data types ('datetime2', 'datetimeoffset' and 'time') If no
+ /// scale is set explicitly it will continue to output scale of 7
+ /// (DEFAULT_VARTIME_SCALE) regardless of switch value.
+ ///
+ /// The default value of this switch is true.
///
public static bool LegacyVarTimeZeroScaleBehaviour =>
AcquireAndReturn(
@@ -256,8 +463,10 @@ public static bool GlobalizationInvariantMode
ref s_legacyVarTimeZeroScaleBehaviour);
///
- /// When enabled, ReadAsync runs asynchronously and does not block the calling thread.
- /// This app context switch defaults to 'false'.
+ /// When enabled, ReadAsync runs asynchronously and does not block the
+ /// calling thread.
+ ///
+ /// The default value of this switch is false.
///
public static bool MakeReadAsyncBlocking =>
AcquireAndReturn(
@@ -266,9 +475,11 @@ public static bool GlobalizationInvariantMode
ref s_makeReadAsyncBlocking);
///
- /// When using Encrypt=false in the connection string, a security warning is output to the console if the TLS version is 1.2 or lower.
- /// This warning can be suppressed by enabling this AppContext switch.
- /// This app context switch defaults to 'false'.
+ /// When using Encrypt=false in the connection string, a security warning is
+ /// output to the console if the TLS version is 1.2 or lower. This warning
+ /// can be suppressed by enabling this AppContext switch.
+ ///
+ /// The default value of this switch is false.
///
public static bool SuppressInsecureTlsWarning =>
AcquireAndReturn(
@@ -277,7 +488,10 @@ public static bool GlobalizationInvariantMode
ref s_suppressInsecureTlsWarning);
///
- /// When set to true, TdsParser will truncate (rather than round) decimal and SqlDecimal values when scaling them.
+ /// When set to true, TdsParser will truncate (rather than round) decimal
+ /// and SqlDecimal values when scaling them.
+ ///
+ /// The default value of this switch is false.
///
public static bool TruncateScaledDecimal =>
AcquireAndReturn(
@@ -286,12 +500,17 @@ public static bool GlobalizationInvariantMode
ref s_truncateScaledDecimal);
///
- /// In TdsParser, the async multi-packet column value fetch behavior can use a continue snapshot state
- /// for improved efficiency. When this switch is enabled (the default), the driver preserves the legacy
- /// compatibility behavior, which does not use the continue snapshot state. When disabled, the new behavior
- /// using the continue snapshot state is enabled. This switch will always return true if
- /// is enabled, because the continue state is not stable without
- /// the multiplexer.
+ /// In TdsParser, the async multi-packet column value fetch behavior can use
+ /// a continue snapshot state for improved efficiency. When this switch is
+ /// enabled (the default), the driver preserves the legacy compatibility
+ /// behavior, which does not use the continue snapshot state. When disabled,
+ /// the new behavior using the continue snapshot state is enabled.
+ ///
+ /// This switch will always return true if
+ /// is enabled, because the
+ /// continue state is not stable without the multiplexer.
+ ///
+ /// The default value of this switch is true.
///
public static bool UseCompatibilityAsyncBehaviour
{
@@ -299,10 +518,11 @@ public static bool UseCompatibilityAsyncBehaviour
{
if (UseCompatibilityProcessSni)
{
- // If ProcessSni compatibility mode has been enabled then the packet
- // multiplexer has been disabled. The new async behaviour using continue
- // point capture is only stable if the multiplexer is enabled so we must
- // return true to enable compatibility async behaviour using only restarts.
+ // If ProcessSni compatibility mode has been enabled then the
+ // packet multiplexer has been disabled. The new async behaviour
+ // using continue point capture is only stable if the
+ // multiplexer is enabled so we must return true to enable
+ // compatibility async behaviour using only restarts.
return true;
}
@@ -314,11 +534,14 @@ public static bool UseCompatibilityAsyncBehaviour
}
///
- /// In TdsParser, the ProcessSni function changed significantly when the packet
- /// multiplexing code needed for high speed multi-packet column values was added.
- /// When this switch is set to true (the default), the old ProcessSni design is used.
- /// When this switch is set to false, the new experimental ProcessSni behavior using
- /// the packet multiplexer is enabled.
+ /// In TdsParser, the ProcessSni function changed significantly when the
+ /// packet multiplexing code needed for high speed multi-packet column
+ /// values was added. When this switch is set to true (the default), the
+ /// old ProcessSni design is used. When this switch is set to false, the
+ /// new experimental ProcessSni behavior using the packet multiplexer is
+ /// enabled.
+ ///
+ /// The default value of this switch is true.
///
public static bool UseCompatibilityProcessSni =>
AcquireAndReturn(
@@ -327,9 +550,11 @@ public static bool UseCompatibilityAsyncBehaviour
ref s_useCompatibilityProcessSni);
///
- /// When set to true, the connection pool will use the new V2 connection pool implementation.
- /// When set to false, the connection pool will use the legacy V1 implementation.
- /// This app context switch defaults to 'false'.
+ /// When set to true, the connection pool will use the new V2 connection
+ /// pool implementation. When set to false, the connection pool will use
+ /// the legacy V1 implementation.
+ ///
+ /// The default value of this switch is false.
///
public static bool UseConnectionPoolV2 =>
AcquireAndReturn(
@@ -339,19 +564,18 @@ public static bool UseCompatibilityAsyncBehaviour
#if NET && _WINDOWS
///
- /// When set to true, .NET Core will use the managed SNI implementation instead of the native SNI implementation.
- ///
- ///
- ///
- /// Non-Windows platforms will always use the managed networking implementation. Windows platforms will use the native SNI
- /// implementation by default, but this can be overridden by setting the AppContext switch.
- ///
- ///
- /// ILLink.Substitutions.xml allows the unused SNI implementation to be trimmed away when the corresponding AppContext
- /// switch is set at compile time. In such cases, this property will return a constant value, even if the AppContext switch is
- /// set or reset at runtime. See the ILLink.Substitutions.Windows.xml and ILLink.Substitutions.Unix.xml resource files for details.
- ///
- ///
+ /// When set to true, .NET on Windows will use the managed SNI
+ /// implementation instead of the native SNI implementation.
+ ///
+ /// ILLink.Substitutions.xml allows the unused SNI implementation to be
+ /// trimmed away when the corresponding AppContext switch is set at compile
+ /// time. In such cases, this property will return a constant value, even if
+ /// the AppContext switch is set or reset at runtime. See the
+ /// ILLink.Substitutions.Windows.xml and ILLink.Substitutions.Unix.xml
+ /// resource files for details.
+ ///
+ /// The default value of this switch is false.
+ ///
public static bool UseManagedNetworking
{
get
@@ -379,20 +603,23 @@ public static bool UseManagedNetworking
}
#elif NET
///
- /// .NET Core on Unix does not support the native SNI, so this will always be true.
+ /// .NET Core on Unix does not support native SNI, so this will always be
+ /// true.
///
public static bool UseManagedNetworking => true;
#else
///
- /// .NET Framework does not support the managed SNI, so this will always be false.
+ /// .NET Framework does not support the managed SNI, so this will always be
+ /// false.
///
public static bool UseManagedNetworking => false;
#endif
///
- /// Specifies minimum login timeout to be set to 1 second instead of 0 seconds,
- /// to prevent a login attempt from waiting indefinitely.
- /// This app context switch defaults to 'true'.
+ /// Specifies minimum login timeout to be set to 1 second instead of 0
+ /// seconds, to prevent a login attempt from waiting indefinitely.
+ ///
+ /// The default value of this switch is true.
///
public static bool UseMinimumLoginTimeout =>
AcquireAndReturn(
diff --git a/src/Microsoft.Data.SqlClient/tests/Common/LocalAppContextSwitchesHelper.cs b/src/Microsoft.Data.SqlClient/tests/Common/LocalAppContextSwitchesHelper.cs
index 507fc53a27..e71d82b398 100644
--- a/src/Microsoft.Data.SqlClient/tests/Common/LocalAppContextSwitchesHelper.cs
+++ b/src/Microsoft.Data.SqlClient/tests/Common/LocalAppContextSwitchesHelper.cs
@@ -64,8 +64,9 @@ public sealed class LocalAppContextSwitchesHelper : IDisposable
///
/// Construct to capture all existing switch values.
///
- /// This call will block, waiting for any previous instance to be disposed
- /// before completing construction.
+ /// This call will block for at most 5 seconds, waiting for any previous
+ /// instance to be disposed before completing construction. Failure to
+ /// acquire the lock in that time will result in an exception being thrown.
///
public LocalAppContextSwitchesHelper()
{
@@ -129,7 +130,8 @@ public LocalAppContextSwitchesHelper()
}
///
- /// Disposal restores all original switch values and releases the instance lock.
+ /// Disposal restores all original switch values and releases the instance
+ /// lock.
///
public void Dispose()
{