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 3872b215ae..ed2c4aedf7 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/LocalAppContextSwitches.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/LocalAppContextSwitches.cs @@ -8,15 +8,23 @@ namespace Microsoft.Data.SqlClient { internal static partial class LocalAppContextSwitches { + private enum Tristate : byte + { + NotInitialized = 0, + False = 1, + 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"; - private static bool? s_legacyRowVersionNullBehavior; - private static bool? s_suppressInsecureTLSWarning; - private static bool s_makeReadAsyncBlocking; - private static bool s_useMinimumLoginTimeout; + // 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; #if !NETFRAMEWORK static LocalAppContextSwitches() @@ -36,7 +44,7 @@ static LocalAppContextSwitches() #if NETFRAMEWORK internal const string DisableTNIRByDefaultString = @"Switch.Microsoft.Data.SqlClient.DisableTNIRByDefaultInConnectionString"; - private static bool s_disableTNIRByDefault; + private static Tristate s_disableTNIRByDefault; /// /// Transparent Network IP Resolution (TNIR) is a revision of the existing MultiSubnetFailover feature. @@ -54,7 +62,23 @@ static LocalAppContextSwitches() /// This app context switch defaults to 'false'. /// public static bool DisableTNIRByDefault - => AppContext.TryGetSwitch(DisableTNIRByDefaultString, out s_disableTNIRByDefault) && s_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 /// @@ -66,13 +90,18 @@ public static bool SuppressInsecureTLSWarning { get { - if (s_suppressInsecureTLSWarning is null) + if (s_suppressInsecureTLSWarning == Tristate.NotInitialized) { - bool result; - result = AppContext.TryGetSwitch(SuppressInsecureTLSWarningString, out result) && result; - s_suppressInsecureTLSWarning = result; + if (AppContext.TryGetSwitch(SuppressInsecureTLSWarningString, out bool returnedValue) && returnedValue) + { + s_suppressInsecureTLSWarning = Tristate.True; + } + else + { + s_suppressInsecureTLSWarning = Tristate.False; + } } - return s_suppressInsecureTLSWarning.Value; + return s_suppressInsecureTLSWarning == Tristate.True; } } @@ -86,13 +115,18 @@ public static bool LegacyRowVersionNullBehavior { get { - if (s_legacyRowVersionNullBehavior is null) + if (s_legacyRowVersionNullBehavior == Tristate.NotInitialized) { - bool result; - result = AppContext.TryGetSwitch(LegacyRowVersionNullString, out result) && result; - s_legacyRowVersionNullBehavior = result; + if (AppContext.TryGetSwitch(LegacyRowVersionNullString, out bool returnedValue) && returnedValue) + { + s_legacyRowVersionNullBehavior = Tristate.True; + } + else + { + s_legacyRowVersionNullBehavior = Tristate.False; + } } - return s_legacyRowVersionNullBehavior.Value; + return s_legacyRowVersionNullBehavior == Tristate.True; } } @@ -101,7 +135,23 @@ public static bool LegacyRowVersionNullBehavior /// This app context switch defaults to 'false'. /// public static bool MakeReadAsyncBlocking - => AppContext.TryGetSwitch(MakeReadAsyncBlockingString, out s_makeReadAsyncBlocking) && s_makeReadAsyncBlocking; + { + get + { + if (s_makeReadAsyncBlocking == Tristate.NotInitialized) + { + if (AppContext.TryGetSwitch(MakeReadAsyncBlockingString, out bool returnedValue) && returnedValue) + { + s_makeReadAsyncBlocking = Tristate.True; + } + else + { + s_makeReadAsyncBlocking = Tristate.False; + } + } + return s_makeReadAsyncBlocking == Tristate.True; + } + } /// /// Specifies minimum login timeout to be set to 1 second instead of 0 seconds, @@ -109,6 +159,22 @@ public static bool MakeReadAsyncBlocking /// This app context switch defaults to 'true'. /// public static bool UseMinimumLoginTimeout - => !AppContext.TryGetSwitch(UseMinimumLoginTimeoutString, out s_useMinimumLoginTimeout) || s_useMinimumLoginTimeout; + { + get + { + if (s_useMinimumLoginTimeout == Tristate.NotInitialized) + { + if (AppContext.TryGetSwitch(UseMinimumLoginTimeoutString, out bool returnedValue) && returnedValue) + { + s_useMinimumLoginTimeout = Tristate.True; + } + else + { + s_useMinimumLoginTimeout = Tristate.False; + } + } + return s_useMinimumLoginTimeout == Tristate.True; + } + } } } 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 6929d35ee7..d00ea1d226 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataReaderTest/DataReaderTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataReaderTest/DataReaderTest.cs @@ -17,6 +17,14 @@ public static class DataReaderTest { private static readonly object s_rowVersionLock = new(); + // this enum must mirror the definition in LocalAppContextSwitches + private enum Tristate : byte + { + NotInitialized = 0, + False = 1, + True = 2 + } + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] public static void LoadReaderIntoDataTableToTestGetSchemaTable() { @@ -261,7 +269,7 @@ public static void CheckNullRowVersionIsBDNull() { lock (s_rowVersionLock) { - bool? originalValue = SetLegacyRowVersionNullBehavior(false); + Tristate originalValue = SetLegacyRowVersionNullBehavior(Tristate.False); try { using SqlConnection con = new(DataTestUtility.TCPConnectionString); @@ -298,7 +306,7 @@ public static void CheckLegacyNullRowVersionIsEmptyArray() { lock (s_rowVersionLock) { - bool? originalValue = SetLegacyRowVersionNullBehavior(true); + Tristate originalValue = SetLegacyRowVersionNullBehavior(Tristate.True); try { using SqlConnection con = new(DataTestUtility.TCPConnectionString); @@ -323,11 +331,11 @@ public static void CheckLegacyNullRowVersionIsEmptyArray() } } - private static bool? SetLegacyRowVersionNullBehavior(bool? value) + private static Tristate SetLegacyRowVersionNullBehavior(Tristate value) { Type switchesType = typeof(SqlCommand).Assembly.GetType("Microsoft.Data.SqlClient.LocalAppContextSwitches"); FieldInfo switchField = switchesType.GetField("s_legacyRowVersionNullBehavior", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static); - bool? originalValue = (bool?)switchField.GetValue(null); + Tristate originalValue = (Tristate)switchField.GetValue(null); switchField.SetValue(null, value); return originalValue; }