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 01f4de3352..4d456aa09c 100644
--- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj
+++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj
@@ -963,6 +963,11 @@
+
+
+ ILLink.Substitutions.xml
+ Resources\ILLink.Substitutions.Windows.xml
+
@@ -1005,6 +1010,11 @@
+
+
+ ILLink.Substitutions.xml
+ Resources\ILLink.Substitutions.Unix.xml
+
diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs
index ce5e960f8d..b1290ea526 100644
--- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs
+++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs
@@ -31,13 +31,6 @@ namespace Microsoft.Data.SqlClient
[DesignerCategory("")]
public sealed partial class SqlConnection : DbConnection, ICloneable
{
- private enum CultureCheckState : uint
- {
- Unknown = 0,
- Standard = 1,
- Invariant = 2
- }
-
private bool _AsyncCommandInProgress;
// SQLStatistics support
@@ -75,9 +68,6 @@ private enum CultureCheckState : uint
// using SqlConnection.Open() method.
internal bool _applyTransientFaultHandling = false;
- // status of invariant culture environment check
- private static CultureCheckState _cultureCheckState;
-
// System column encryption key store providers are added by default
private static readonly Dictionary s_systemColumnEncryptionKeyStoreProviders
= new(capacity: 3, comparer: StringComparer.OrdinalIgnoreCase)
@@ -1956,48 +1946,9 @@ private bool TryOpen(TaskCompletionSource retry, SqlConnec
{
SqlConnectionString connectionOptions = (SqlConnectionString)ConnectionOptions;
- if (_cultureCheckState != CultureCheckState.Standard)
+ if (LocalAppContextSwitches.GlobalizationInvariantMode)
{
- // .NET Core 2.0 and up supports a Globalization Invariant Mode to reduce the size of
- // 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. Throwing on open with a meaningful message helps identify the issue.
- if (_cultureCheckState == CultureCheckState.Unknown)
- {
- // check if invariant state has been set by appcontext switch directly
- if (AppContext.TryGetSwitch("System.Globalization.Invariant", out bool isEnabled) && isEnabled)
- {
- _cultureCheckState = CultureCheckState.Invariant;
- }
- else
- {
- // check if invariant state has been set through environment variables
- string envValue = Environment.GetEnvironmentVariable("DOTNET_SYSTEM_GLOBALIZATION_INVARIANT");
- if (string.Equals(envValue, bool.TrueString, StringComparison.OrdinalIgnoreCase) || string.Equals(envValue, "1", StringComparison.OrdinalIgnoreCase))
- {
- _cultureCheckState = CultureCheckState.Invariant;
- }
- else
- {
- // if it hasn't been manually set it could still apply if the os doesn't have
- // icu libs installed or is a native binary with icu support trimmed away
- // netcore 3.1 to net5 do not throw in attempting to create en-us in inariant mode
- // net6 and greater will throw so catch and infer invariant mode from the exception
- try
- {
- _cultureCheckState = CultureInfo.GetCultureInfo("en-US").EnglishName.Contains("Invariant") ? CultureCheckState.Invariant : CultureCheckState.Standard;
- }
- catch (CultureNotFoundException)
- {
- _cultureCheckState = CultureCheckState.Invariant;
- }
- }
- }
- }
- if (_cultureCheckState == CultureCheckState.Invariant)
- {
- throw SQL.GlobalizationInvariantModeNotSupported();
- }
+ throw SQL.GlobalizationInvariantModeNotSupported();
}
_applyTransientFaultHandling = (!overrides.HasFlag(SqlConnectionOverrides.OpenWithoutRetry) && connectionOptions != null && connectionOptions.ConnectRetryCount > 0);
diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.Windows.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.Windows.cs
index 2c924b3b79..8074fda5a7 100644
--- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.Windows.cs
+++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.Windows.cs
@@ -11,7 +11,7 @@ internal sealed partial class TdsParser
{
internal void PostReadAsyncForMars()
{
- if (TdsParserStateObjectFactory.UseManagedSNI)
+ if (LocalAppContextSwitches.UseManagedNetworking)
return;
// HACK HACK HACK - for Async only
diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs
index 4b6a82b7ba..1925fd756b 100644
--- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs
+++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs
@@ -69,7 +69,6 @@ internal sealed partial class TdsParser
// Constants
private const int constBinBufferSize = 4096; // Size of the buffer used to read input parameter of type Stream
private const int constTextBufferSize = 4096; // Size of the buffer (in chars) user to read input parameter of type TextReader
- private const string enableTruncateSwitch = "Switch.Microsoft.Data.SqlClient.TruncateScaledDecimal"; // for applications that need to maintain backwards compatibility with the previous behavior
// State variables
internal TdsParserState _state = TdsParserState.Closed; // status flag for connection
@@ -204,16 +203,6 @@ internal SqlInternalConnectionTds Connection
}
}
- private static bool EnableTruncateSwitch
- {
- get
- {
- bool value;
- value = AppContext.TryGetSwitch(enableTruncateSwitch, out value) ? value : false;
- return value;
- }
- }
-
internal SqlInternalTransaction CurrentTransaction
{
get
@@ -631,7 +620,7 @@ internal void EnableMars()
// Cache physical stateObj and connection.
_pMarsPhysicalConObj = _physicalStateObj;
- if (TdsParserStateObjectFactory.UseManagedSNI)
+ if (LocalAppContextSwitches.UseManagedNetworking)
_pMarsPhysicalConObj.IncrementPendingCallbacks();
uint info = 0;
@@ -1489,7 +1478,7 @@ internal SqlError ProcessSNIError(TdsParserStateObject stateObj)
* !=null | == 0 | replace text left of errorMessage
*/
- if (TdsParserStateObjectFactory.UseManagedSNI)
+ if (LocalAppContextSwitches.UseManagedNetworking)
{
Debug.Assert(!string.IsNullOrEmpty(details.ErrorMessage) || details.SniErrorNumber != 0, "Empty error message received from SNI");
SqlClientEventSource.Log.TryAdvancedTraceEvent(" Empty error message received from SNI. Error Message = {0}, SNI Error Number ={1}", details.ErrorMessage, details.SniErrorNumber);
@@ -1538,7 +1527,7 @@ internal SqlError ProcessSNIError(TdsParserStateObject stateObj)
}
else
{
- if (TdsParserStateObjectFactory.UseManagedSNI)
+ if (LocalAppContextSwitches.UseManagedNetworking)
{
// SNI error. Append additional error message info if available and hasn't been included.
string sniLookupMessage = SQL.GetSNIErrorMessage(details.SniErrorNumber);
@@ -7653,7 +7642,7 @@ internal static SqlDecimal AdjustSqlDecimalScale(SqlDecimal d, int newScale)
{
if (d.Scale != newScale)
{
- bool round = !EnableTruncateSwitch;
+ bool round = !LocalAppContextSwitches.TruncateScaledDecimal;
return SqlDecimal.AdjustScale(d, newScale - d.Scale, round);
}
@@ -7666,7 +7655,7 @@ internal static decimal AdjustDecimalScale(decimal value, int newScale)
if (newScale != oldScale)
{
- bool round = !EnableTruncateSwitch;
+ bool round = !LocalAppContextSwitches.TruncateScaledDecimal;
SqlDecimal num = new SqlDecimal(value);
num = SqlDecimal.AdjustScale(num, newScale - oldScale, round);
return num.Value;
diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs
index f89613204f..e8b054ba40 100644
--- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs
+++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs
@@ -69,7 +69,6 @@ internal sealed partial class TdsParser
// Constants
private const int constBinBufferSize = 4096; // Size of the buffer used to read input parameter of type Stream
private const int constTextBufferSize = 4096; // Size of the buffer (in chars) user to read input parameter of type TextReader
- private const string enableTruncateSwitch = "Switch.Microsoft.Data.SqlClient.TruncateScaledDecimal"; // for applications that need to maintain backwards compatibility with the previous behavior
// State variables
internal TdsParserState _state = TdsParserState.Closed; // status flag for connection
@@ -205,16 +204,6 @@ internal SqlInternalConnectionTds Connection
}
}
- private static bool EnableTruncateSwitch
- {
- get
- {
- bool value;
- value = AppContext.TryGetSwitch(enableTruncateSwitch, out value) ? value : false;
- return value;
- }
- }
-
internal SqlInternalTransaction CurrentTransaction
{
get
@@ -7849,7 +7838,7 @@ internal static SqlDecimal AdjustSqlDecimalScale(SqlDecimal d, int newScale)
{
if (d.Scale != newScale)
{
- bool round = !EnableTruncateSwitch;
+ bool round = !LocalAppContextSwitches.TruncateScaledDecimal;
return SqlDecimal.AdjustScale(d, newScale - d.Scale, round);
}
@@ -7862,7 +7851,7 @@ internal static decimal AdjustDecimalScale(decimal value, int newScale)
if (newScale != oldScale)
{
- bool round = !EnableTruncateSwitch;
+ bool round = !LocalAppContextSwitches.TruncateScaledDecimal;
SqlDecimal num = new SqlDecimal(value);
num = SqlDecimal.AdjustScale(num, newScale - oldScale, round);
return num.Value;
diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Sql/SqlDataSourceEnumerator.Windows.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Sql/SqlDataSourceEnumerator.Windows.cs
index 25caa8911b..f92e9beb32 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Sql/SqlDataSourceEnumerator.Windows.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Sql/SqlDataSourceEnumerator.Windows.cs
@@ -15,7 +15,7 @@ private partial DataTable GetDataSourcesInternal()
#if NETFRAMEWORK
return SqlDataSourceEnumeratorNativeHelper.GetDataSources();
#else
- return SqlClient.TdsParserStateObjectFactory.UseManagedSNI ? SqlDataSourceEnumeratorManagedHelper.GetDataSources() : SqlDataSourceEnumeratorNativeHelper.GetDataSources();
+ return SqlClient.LocalAppContextSwitches.UseManagedNetworking ? SqlDataSourceEnumeratorManagedHelper.GetDataSources() : SqlDataSourceEnumeratorNativeHelper.GetDataSources();
#endif
}
}
diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/DbConnectionPoolIdentity.Windows.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/DbConnectionPoolIdentity.Windows.cs
index 99783ff3c7..0c6fd07503 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/DbConnectionPoolIdentity.Windows.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/DbConnectionPoolIdentity.Windows.cs
@@ -28,7 +28,7 @@ internal static WindowsIdentity GetCurrentWindowsIdentity()
#else
internal static DbConnectionPoolIdentity GetCurrent()
{
- return TdsParserStateObjectFactory.UseManagedSNI ? GetCurrentManaged() : GetCurrentNative();
+ return LocalAppContextSwitches.UseManagedNetworking ? GetCurrentManaged() : GetCurrentNative();
}
#endif
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 dfd94453f2..ee5d088dc4 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/LocalAppContextSwitches.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/LocalAppContextSwitches.cs
@@ -15,14 +15,22 @@ private enum Tristate : byte
True = 2
}
- internal const string MakeReadAsyncBlockingString = @"Switch.Microsoft.Data.SqlClient.MakeReadAsyncBlocking";
- internal const string LegacyRowVersionNullString = @"Switch.Microsoft.Data.SqlClient.LegacyRowVersionNullBehavior";
- internal const string SuppressInsecureTlsWarningString = @"Switch.Microsoft.Data.SqlClient.SuppressInsecureTLSWarning";
- internal const string UseMinimumLoginTimeoutString = @"Switch.Microsoft.Data.SqlClient.UseOneSecFloorInTimeoutCalculationDuringLogin";
- internal const string LegacyVarTimeZeroScaleBehaviourString = @"Switch.Microsoft.Data.SqlClient.LegacyVarTimeZeroScaleBehaviour";
- internal const string UseCompatibilityProcessSniString = @"Switch.Microsoft.Data.SqlClient.UseCompatibilityProcessSni";
- internal const string UseCompatibilityAsyncBehaviourString = @"Switch.Microsoft.Data.SqlClient.UseCompatibilityAsyncBehaviour";
- internal const string UseConnectionPoolV2String = @"Switch.Microsoft.Data.SqlClient.UseConnectionPoolV2";
+ 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";
+#if NET
+ private const string GlobalizationInvariantModeString = @"System.Globalization.Invariant";
+ private const string GlobalizationInvariantModeEnvironmentVariable = "DOTNET_SYSTEM_GLOBALIZATION_INVARIANT";
+ private const string UseManagedNetworkingOnWindowsString = "Switch.Microsoft.Data.SqlClient.UseManagedNetworkingOnWindows";
+#else
+ 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
private static Tristate s_legacyRowVersionNullBehavior;
@@ -34,6 +42,13 @@ private enum Tristate : byte
private static Tristate s_useCompatibilityProcessSni;
private static Tristate s_useCompatibilityAsyncBehaviour;
private static Tristate s_useConnectionPoolV2;
+ private static Tristate s_truncateScaledDecimal;
+#if NET
+ private static Tristate s_globalizationInvariantMode;
+ private static Tristate s_useManagedNetworking;
+#else
+ private static Tristate s_disableTnirByDefault;
+#endif
#if NET
static LocalAppContextSwitches()
@@ -51,44 +66,6 @@ static LocalAppContextSwitches()
}
#endif
-#if NETFRAMEWORK
- internal const string DisableTnirByDefaultString = @"Switch.Microsoft.Data.SqlClient.DisableTNIRByDefaultInConnectionString";
- private static Tristate s_disableTnirByDefault;
-
- ///
- /// 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
- {
- if (s_disableTnirByDefault == Tristate.NotInitialized)
- {
- if (AppContext.TryGetSwitch(DisableTnirByDefaultString, out bool returnedValue) && returnedValue)
- {
- s_disableTnirByDefault = Tristate.True;
- }
- else
- {
- s_disableTnirByDefault = Tristate.False;
- }
- }
- return s_disableTnirByDefault == Tristate.True;
- }
- }
-#endif
///
/// In TdsParser the ProcessSni function changed significantly when the packet
/// multiplexing code needed for high speed multi-packet column values was added.
@@ -296,5 +273,159 @@ public static bool UseConnectionPoolV2
return s_useConnectionPoolV2 == Tristate.True;
}
}
+
+ ///
+ /// 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 == Tristate.NotInitialized)
+ {
+ if (AppContext.TryGetSwitch(TruncateScaledDecimalString, out bool returnedValue) && returnedValue)
+ {
+ s_truncateScaledDecimal = Tristate.True;
+ }
+ else
+ {
+ s_truncateScaledDecimal = Tristate.False;
+ }
+ }
+ return s_truncateScaledDecimal == Tristate.True;
+ }
+ }
+
+#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
+ {
+ if (s_globalizationInvariantMode == Tristate.NotInitialized)
+ {
+ // Check if invariant mode is has been set by the AppContext switch directly
+ if (AppContext.TryGetSwitch(GlobalizationInvariantModeString, out bool returnedValue) && returnedValue)
+ {
+ s_globalizationInvariantMode = Tristate.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;
+ }
+ 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")
+ ? 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;
+ }
+ }
+ }
+ }
+ return s_globalizationInvariantMode == Tristate.True;
+ }
+ }
+
+ ///
+ /// 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
+ {
+ if (s_useManagedNetworking == Tristate.NotInitialized)
+ {
+ 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;
+ }
+ }
+ return s_useManagedNetworking == Tristate.True;
+ }
+ }
+#else
+ ///
+ /// .NET Framework does not support Globalization Invariant mode, so this will always be false.
+ ///
+ public const bool GlobalizationInvariantMode = false;
+
+ ///
+ /// .NET Framework does not support the managed SNI, so this will always be false.
+ ///
+ public const bool UseManagedNetworking = false;
+
+ ///
+ /// 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
+ {
+ if (s_disableTnirByDefault == Tristate.NotInitialized)
+ {
+ if (AppContext.TryGetSwitch(DisableTnirByDefaultString, out bool returnedValue) && returnedValue)
+ {
+ s_disableTnirByDefault = Tristate.True;
+ }
+ else
+ {
+ s_disableTnirByDefault = Tristate.False;
+ }
+ }
+ return s_disableTnirByDefault == Tristate.True;
+ }
+ }
+#endif
}
}
diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs
index 06d679e22a..bcb7fc9005 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs
@@ -3224,7 +3224,7 @@ internal void ReadSni(TaskCompletionSource
public bool ClientOSEncryptionSupport =>
#if NET
- UseManagedSNI ? ManagedSni.SniLoadHandle.SingletonInstance.ClientOSEncryptionSupport : SNILoadHandle.SingletonInstance.ClientOSEncryptionSupport;
+ LocalAppContextSwitches.UseManagedNetworking ? ManagedSni.SniLoadHandle.SingletonInstance.ClientOSEncryptionSupport : SNILoadHandle.SingletonInstance.ClientOSEncryptionSupport;
#else
SNILoadHandle.SingletonInstance.ClientOSEncryptionSupport;
#endif
@@ -52,16 +40,14 @@ internal sealed class TdsParserStateObjectFactory
public TdsParserStateObject CreateTdsParserStateObject(TdsParser parser)
{
#if NET
- if (UseManagedSNI)
+ if (LocalAppContextSwitches.UseManagedNetworking)
{
- SqlClientEventSource.Log.TryTraceEvent("TdsParserStateObjectFactory.CreateTdsParserStateObject | Info | Found AppContext switch '{0}' enabled, managed networking implementation will be used."
- , UseManagedNetworkingOnWindows);
+ SqlClientEventSource.Log.TryTraceEvent("TdsParserStateObjectFactory.CreateTdsParserStateObject | Info | Using managed networking implementation.");
return new TdsParserStateObjectManaged(parser);
}
else
{
- SqlClientEventSource.Log.TryTraceEvent("TdsParserStateObjectFactory.CreateTdsParserStateObject | Info | AppContext switch '{0}' not enabled, native networking implementation will be used."
- , UseManagedNetworkingOnWindows);
+ SqlClientEventSource.Log.TryTraceEvent("TdsParserStateObjectFactory.CreateTdsParserStateObject | Info | Using native networking implementation.");
return new TdsParserStateObjectNative(parser);
}
#else
@@ -72,7 +58,7 @@ public TdsParserStateObject CreateTdsParserStateObject(TdsParser parser)
internal TdsParserStateObject CreateSessionObject(TdsParser tdsParser, TdsParserStateObject _pMarsPhysicalConObj, bool v)
{
#if NET
- if (TdsParserStateObjectFactory.UseManagedSNI)
+ if (LocalAppContextSwitches.UseManagedNetworking)
{
return new TdsParserStateObjectManaged(tdsParser, _pMarsPhysicalConObj, true);
}
diff --git a/src/Microsoft.Data.SqlClient/src/Resources/ILLink.Substitutions.Unix.xml b/src/Microsoft.Data.SqlClient/src/Resources/ILLink.Substitutions.Unix.xml
new file mode 100644
index 0000000000..4e105d25f0
--- /dev/null
+++ b/src/Microsoft.Data.SqlClient/src/Resources/ILLink.Substitutions.Unix.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/src/Microsoft.Data.SqlClient/src/Resources/ILLink.Substitutions.Windows.xml b/src/Microsoft.Data.SqlClient/src/Resources/ILLink.Substitutions.Windows.xml
new file mode 100644
index 0000000000..e8577c7ae0
--- /dev/null
+++ b/src/Microsoft.Data.SqlClient/src/Resources/ILLink.Substitutions.Windows.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/Microsoft.Data.SqlClient/tests/Common/LocalAppContextSwitchesHelper.cs b/src/Microsoft.Data.SqlClient/tests/Common/LocalAppContextSwitchesHelper.cs
index 2970b1f1ce..df68f86677 100644
--- a/src/Microsoft.Data.SqlClient/tests/Common/LocalAppContextSwitchesHelper.cs
+++ b/src/Microsoft.Data.SqlClient/tests/Common/LocalAppContextSwitchesHelper.cs
@@ -30,7 +30,11 @@ public sealed class LocalAppContextSwitchesHelper : IDisposable
private readonly PropertyInfo _useCompatibilityProcessSniProperty;
private readonly PropertyInfo _useCompatibilityAsyncBehaviourProperty;
private readonly PropertyInfo _useConnectionPoolV2Property;
- #if NETFRAMEWORK
+ private readonly PropertyInfo _truncateScaledDecimalProperty;
+ #if NET
+ private readonly PropertyInfo _globalizationInvariantModeProperty;
+ private readonly PropertyInfo _useManagedNetworkingProperty;
+ #else
private readonly PropertyInfo _disableTnirByDefaultProperty;
#endif
@@ -51,7 +55,14 @@ public sealed class LocalAppContextSwitchesHelper : IDisposable
private readonly Tristate _useCompatibilityAsyncBehaviourOriginal;
private readonly FieldInfo _useConnectionPoolV2Field;
private readonly Tristate _useConnectionPoolV2Original;
- #if NETFRAMEWORK
+ private readonly FieldInfo _truncateScaledDecimalField;
+ private readonly Tristate _truncateScaledDecimalOriginal;
+ #if NET
+ private readonly FieldInfo _globalizationInvariantModeField;
+ private readonly Tristate _globalizationInvariantModeOriginal;
+ private readonly FieldInfo _useManagedNetworkingField;
+ private readonly Tristate _useManagedNetworkingOriginal;
+ #else
private readonly FieldInfo _disableTnirByDefaultField;
private readonly Tristate _disableTnirByDefaultOriginal;
#endif
@@ -140,7 +151,19 @@ void InitProperty(string name, out PropertyInfo property)
"UseConnectionPoolV2",
out _useConnectionPoolV2Property);
- #if NETFRAMEWORK
+ InitProperty(
+ "TruncateScaledDecimal",
+ out _truncateScaledDecimalProperty);
+
+ #if NET
+ InitProperty(
+ "GlobalizationInvariantMode",
+ out _globalizationInvariantModeProperty);
+
+ InitProperty(
+ "UseManagedNetworking",
+ out _useManagedNetworkingProperty);
+ #else
InitProperty(
"DisableTnirByDefault",
out _disableTnirByDefaultProperty);
@@ -201,7 +224,22 @@ void InitField(string name, out FieldInfo field, out Tristate value)
out _useConnectionPoolV2Field,
out _useConnectionPoolV2Original);
- #if NETFRAMEWORK
+ InitField(
+ "s_truncateScaledDecimal",
+ out _truncateScaledDecimalField,
+ out _truncateScaledDecimalOriginal);
+
+ #if NET
+ InitField(
+ "s_globalizationInvariantMode",
+ out _globalizationInvariantModeField,
+ out _globalizationInvariantModeOriginal);
+
+ InitField(
+ "s_useManagedNetworking",
+ out _useManagedNetworkingField,
+ out _useManagedNetworkingOriginal);
+ #else
InitField(
"s_disableTnirByDefault",
out _disableTnirByDefaultField,
@@ -265,7 +303,19 @@ void RestoreField(FieldInfo field, Tristate value)
_useConnectionPoolV2Field,
_useConnectionPoolV2Original);
- #if NETFRAMEWORK
+ RestoreField(
+ _truncateScaledDecimalField,
+ _truncateScaledDecimalOriginal);
+
+ #if NET
+ RestoreField(
+ _globalizationInvariantModeField,
+ _globalizationInvariantModeOriginal);
+
+ RestoreField(
+ _useManagedNetworkingField,
+ _useManagedNetworkingOriginal);
+ #else
RestoreField(
_disableTnirByDefaultField,
_disableTnirByDefaultOriginal);
@@ -350,7 +400,31 @@ public bool UseConnectionPoolV2
get => (bool)_useConnectionPoolV2Property.GetValue(null);
}
- #if NETFRAMEWORK
+ ///
+ /// Access the LocalAppContextSwitches.TruncateScaledDecimal property.
+ ///
+ public bool TruncateScaledDecimal
+ {
+ get => (bool)_truncateScaledDecimalProperty.GetValue(null);
+ }
+
+ #if NET
+ ///
+ /// Access the LocalAppContextSwitches.GlobalizationInvariantMode property.
+ ///
+ public bool GlobalizationInvariantMode
+ {
+ get => (bool)_globalizationInvariantModeProperty.GetValue(null);
+ }
+
+ ///
+ /// Access the LocalAppContextSwitches.UseManagedNetworking property.
+ ///
+ public bool UseManagedNetworking
+ {
+ get => (bool)_useManagedNetworkingProperty.GetValue(null);
+ }
+ #else
///
/// Access the LocalAppContextSwitches.DisableTnirByDefault property.
///
@@ -443,7 +517,34 @@ public Tristate UseConnectionPoolV2Field
set => SetValue(_useConnectionPoolV2Field, value);
}
- #if NETFRAMEWORK
+ ///
+ /// Get or set the LocalAppContextSwitches.TruncateScaledDecimal switch value.
+ ///
+ public Tristate TruncateScaledDecimalField
+ {
+ get => GetValue(_truncateScaledDecimalField);
+ set => SetValue(_truncateScaledDecimalField, value);
+ }
+
+ #if NET
+ ///
+ /// Get or set the LocalAppContextSwitches.GlobalizationInvariantMode switch value.
+ ///
+ public Tristate GlobalizationInvariantModeField
+ {
+ get => GetValue(_globalizationInvariantModeField);
+ set => SetValue(_globalizationInvariantModeField, value);
+ }
+
+ ///
+ /// Get or set the LocalAppContextSwitches.UseManagedNetworking switch value.
+ ///
+ public Tristate UseManagedNetworkingField
+ {
+ get => GetValue(_useManagedNetworkingField);
+ set => SetValue(_useManagedNetworkingField, value);
+ }
+ #else
///
/// Get or set the LocalAppContextSwitches.DisableTnirByDefault switch
/// value.
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 bf99691907..d98fe6abd4 100644
--- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/ParametersTest.cs
+++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/ParametersTest.cs
@@ -11,6 +11,8 @@
using System.Threading.Tasks;
using Xunit;
using System.Globalization;
+using Microsoft.Data.SqlClient.Tests.Common;
+
#if !NETFRAMEWORK
using Microsoft.SqlServer.Types;
@@ -570,13 +572,16 @@ public static void SqlDecimalConvertToDecimal_TestOutOfRange(string sqlDecimalVa
[ClassData(typeof(ConnectionStringsProvider))]
public static void TestScaledDecimalParameter_CommandInsert(string connectionString, bool truncateScaledDecimal)
{
+ using LocalAppContextSwitchesHelper appContextSwitchesHelper = new();
+
string tableName = DataTestUtility.GetUniqueNameForSqlServer("TestDecimalParameterCMD");
using SqlConnection connection = InitialDatabaseTable(connectionString, tableName);
try
{
using (SqlCommand cmd = connection.CreateCommand())
{
- AppContext.SetSwitch(TruncateDecimalSwitch, truncateScaledDecimal);
+ appContextSwitchesHelper.TruncateScaledDecimalField = truncateScaledDecimal ? LocalAppContextSwitchesHelper.Tristate.True : LocalAppContextSwitchesHelper.Tristate.False;
+
var p = new SqlParameter("@Value", null)
{
Precision = 18,
@@ -602,6 +607,8 @@ public static void TestScaledDecimalParameter_CommandInsert(string connectionStr
[ClassData(typeof(ConnectionStringsProvider))]
public static void TestScaledDecimalParameter_BulkCopy(string connectionString, bool truncateScaledDecimal)
{
+ using LocalAppContextSwitchesHelper appContextSwitchesHelper = new();
+
string tableName = DataTestUtility.GetUniqueNameForSqlServer("TestDecimalParameterBC");
using SqlConnection connection = InitialDatabaseTable(connectionString, tableName);
try
@@ -620,7 +627,7 @@ public static void TestScaledDecimalParameter_BulkCopy(string connectionString,
}
bulkCopy.DestinationTableName = tableName;
- AppContext.SetSwitch(TruncateDecimalSwitch, truncateScaledDecimal);
+ appContextSwitchesHelper.TruncateScaledDecimalField = truncateScaledDecimal ? LocalAppContextSwitchesHelper.Tristate.True : LocalAppContextSwitchesHelper.Tristate.False;
bulkCopy.WriteToServer(table);
}
Assert.True(ValidateInsertedValues(connection, tableName, truncateScaledDecimal), $"Invalid test happened with connection string [{connection.ConnectionString}]");
@@ -636,6 +643,8 @@ public static void TestScaledDecimalParameter_BulkCopy(string connectionString,
[ClassData(typeof(ConnectionStringsProvider))]
public static void TestScaledDecimalTVP_CommandSP(string connectionString, bool truncateScaledDecimal)
{
+ using LocalAppContextSwitchesHelper appContextSwitchesHelper = new();
+
string tableName = DataTestUtility.GetUniqueNameForSqlServer("TestDecimalParameterBC");
string tableTypeName = DataTestUtility.GetUniqueNameForSqlServer("UDTTTestDecimalParameterBC");
string spName = DataTestUtility.GetUniqueNameForSqlServer("spTestDecimalParameterBC");
@@ -663,7 +672,7 @@ public static void TestScaledDecimalTVP_CommandSP(string connectionString, bool
table.Rows.Add(newRow);
}
p.Value = table;
- AppContext.SetSwitch(TruncateDecimalSwitch, truncateScaledDecimal);
+ appContextSwitchesHelper.TruncateScaledDecimalField = truncateScaledDecimal ? LocalAppContextSwitchesHelper.Tristate.True : LocalAppContextSwitchesHelper.Tristate.False;
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 72bab47869..117ab67c0d 100644
--- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/AdjustPrecScaleForBulkCopy.cs
+++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/AdjustPrecScaleForBulkCopy.cs
@@ -5,6 +5,7 @@
using System;
using System.Data;
using System.Data.SqlTypes;
+using Microsoft.Data.SqlClient.Tests.Common;
using Xunit;
namespace Microsoft.Data.SqlClient.ManualTesting.Tests
@@ -14,6 +15,7 @@ public static class AdjustPrecScaleForBulkCopy
[ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))]
public static void RunTest()
{
+ using LocalAppContextSwitchesHelper appContextSwitches = new();
SqlDecimal value = BulkCopySqlDecimalToTable(new SqlDecimal(0), 1, 0, 2, 2);
Assert.Equal("0.00", value.ToString());
@@ -27,7 +29,7 @@ public static void RunTest()
Assert.Equal("12.3", value.ToString());
value = BulkCopySqlDecimalToTable(new SqlDecimal(123.45), 10, 2, 4, 1);
- if (AppContext.TryGetSwitch("Switch.Microsoft.Data.SqlClient.TruncateScaledDecimal", out bool switchValue) && switchValue)
+ if (appContextSwitches.TruncateScaledDecimal)
{
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 90ee094f8f..ec60f0d3cd 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
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
+using System;
using Xunit;
namespace Microsoft.Data.SqlClient.UnitTests;
@@ -25,8 +26,12 @@ public void TestDefaultAppContextSwitchValues()
Assert.False(LocalAppContextSwitches.UseCompatibilityProcessSni);
Assert.False(LocalAppContextSwitches.UseCompatibilityAsyncBehaviour);
Assert.False(LocalAppContextSwitches.UseConnectionPoolV2);
- #if NETFRAMEWORK
+ Assert.False(LocalAppContextSwitches.TruncateScaledDecimal);
+#if NETFRAMEWORK
Assert.False(LocalAppContextSwitches.DisableTnirByDefault);
- #endif
+ Assert.False(LocalAppContextSwitches.UseManagedNetworking);
+#else
+ Assert.Equal(!OperatingSystem.IsWindows(), LocalAppContextSwitches.UseManagedNetworking);
+#endif
}
}