diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/ConnectionString/DbConnectionStringDefaults.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/ConnectionString/DbConnectionStringDefaults.cs
index 757b3522fc..460fa0c2cc 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/ConnectionString/DbConnectionStringDefaults.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/ConnectionString/DbConnectionStringDefaults.cs
@@ -39,7 +39,7 @@ internal static class DbConnectionStringDefaults
internal const int MaxPoolSize = 100;
internal const int MinPoolSize = 0;
internal const bool MultipleActiveResultSets = false;
- internal const bool MultiSubnetFailover = false;
+ internal static bool MultiSubnetFailover => LocalAppContextSwitches.EnableMultiSubnetFailoverByDefault;
internal const int PacketSize = 8000;
internal const string Password = "";
internal const bool PersistSecurityInfo = false;
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 a12587411c..09d84e0ea7 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/LocalAppContextSwitches.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/LocalAppContextSwitches.cs
@@ -27,6 +27,7 @@ private enum Tristate : byte
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 NET
private const string GlobalizationInvariantModeString = @"System.Globalization.Invariant";
@@ -52,6 +53,7 @@ private enum Tristate : byte
private static Tristate s_truncateScaledDecimal;
private static Tristate s_ignoreServerProvidedFailoverPartner;
private static Tristate s_enableUserAgent;
+ private static Tristate s_multiSubnetFailoverByDefault;
#if NET
private static Tristate s_globalizationInvariantMode;
@@ -511,6 +513,31 @@ public static bool DisableTnirByDefault
return s_disableTnirByDefault == Tristate.True;
}
}
- #endif
+#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
+ {
+ get
+ {
+ if (s_multiSubnetFailoverByDefault == Tristate.NotInitialized)
+ {
+ if (AppContext.TryGetSwitch(EnableMultiSubnetFailoverByDefaultString, out bool returnedValue) && returnedValue)
+ {
+ s_multiSubnetFailoverByDefault = Tristate.True;
+ }
+ else
+ {
+ s_multiSubnetFailoverByDefault = Tristate.False;
+ }
+ }
+ return s_multiSubnetFailoverByDefault == Tristate.True;
+ }
+ }
}
}
diff --git a/src/Microsoft.Data.SqlClient/tests/Common/LocalAppContextSwitchesHelper.cs b/src/Microsoft.Data.SqlClient/tests/Common/LocalAppContextSwitchesHelper.cs
index 1798e1fcf4..7d90df85fe 100644
--- a/src/Microsoft.Data.SqlClient/tests/Common/LocalAppContextSwitchesHelper.cs
+++ b/src/Microsoft.Data.SqlClient/tests/Common/LocalAppContextSwitchesHelper.cs
@@ -33,8 +33,8 @@ public sealed class LocalAppContextSwitchesHelper : IDisposable
private readonly PropertyInfo _truncateScaledDecimalProperty;
private readonly PropertyInfo _ignoreServerProvidedFailoverPartner;
private readonly PropertyInfo _enableUserAgent;
-
- #if NET
+ private readonly PropertyInfo _enableMultiSubnetFailoverByDefaultProperty;
+#if NET
private readonly PropertyInfo _globalizationInvariantModeProperty;
#endif
@@ -69,8 +69,9 @@ public sealed class LocalAppContextSwitchesHelper : IDisposable
private readonly Tristate _ignoreServerProvidedFailoverPartnerOriginal;
private readonly FieldInfo _enableUserAgentField;
private readonly Tristate _enableUserAgentOriginal;
-
- #if NET
+ private readonly FieldInfo _multiSubnetFailoverByDefaultField;
+ private readonly Tristate _multiSubnetFailoverByDefaultOriginal;
+#if NET
private readonly FieldInfo _globalizationInvariantModeField;
private readonly Tristate _globalizationInvariantModeOriginal;
#endif
@@ -181,7 +182,11 @@ void InitProperty(string name, out PropertyInfo property)
"EnableUserAgent",
out _enableUserAgent);
- #if NET
+ InitProperty(
+ "EnableMultiSubnetFailoverByDefault",
+ out _enableMultiSubnetFailoverByDefaultProperty);
+
+#if NET
InitProperty(
"GlobalizationInvariantMode",
out _globalizationInvariantModeProperty);
@@ -269,7 +274,12 @@ void InitField(string name, out FieldInfo field, out Tristate value)
out _enableUserAgentField,
out _enableUserAgentOriginal);
- #if NET
+ InitField(
+ "s_multiSubnetFailoverByDefault",
+ out _multiSubnetFailoverByDefaultField,
+ out _multiSubnetFailoverByDefaultOriginal);
+
+#if NET
InitField(
"s_globalizationInvariantMode",
out _globalizationInvariantModeField,
@@ -281,8 +291,8 @@ void InitField(string name, out FieldInfo field, out Tristate value)
"s_useManagedNetworking",
out _useManagedNetworkingField,
out _useManagedNetworkingOriginal);
- #endif
-
+#endif
+
#if NETFRAMEWORK
InitField(
"s_disableTnirByDefault",
@@ -359,6 +369,10 @@ void RestoreField(FieldInfo field, Tristate value)
_enableUserAgentField,
_enableUserAgentOriginal);
+ RestoreField(
+ _multiSubnetFailoverByDefaultField,
+ _multiSubnetFailoverByDefaultOriginal);
+
#if NET
RestoreField(
_globalizationInvariantModeField,
@@ -474,6 +488,11 @@ public bool EnableUserAgent
get => (bool)_enableUserAgent.GetValue(null);
}
+ public bool EnableMultiSubnetFailoverByDefault
+ {
+ get => (bool)_enableMultiSubnetFailoverByDefaultProperty.GetValue(null);
+ }
+
#if NET
///
/// Access the LocalAppContextSwitches.GlobalizationInvariantMode property.
@@ -608,7 +627,13 @@ public Tristate EnableUserAgentField
set => SetValue(_enableUserAgentField, value);
}
- #if NET
+ public Tristate EnableMultiSubnetFailoverByDefaultField
+ {
+ get => GetValue(_multiSubnetFailoverByDefaultField);
+ set => SetValue(_multiSubnetFailoverByDefaultField, value);
+ }
+
+#if NET
///
/// Get or set the LocalAppContextSwitches.GlobalizationInvariantMode switch value.
///
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 c28fe18978..c92402af41 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,6 +27,7 @@ public void TestDefaultAppContextSwitchValues()
Assert.True(LocalAppContextSwitches.UseCompatibilityAsyncBehaviour);
Assert.False(LocalAppContextSwitches.UseConnectionPoolV2);
Assert.False(LocalAppContextSwitches.TruncateScaledDecimal);
+ Assert.False(LocalAppContextSwitches.EnableMultiSubnetFailoverByDefault);
#if NETFRAMEWORK
Assert.False(LocalAppContextSwitches.DisableTnirByDefault);
Assert.False(LocalAppContextSwitches.UseManagedNetworking);
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 14bc51d520..e413821932 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
@@ -58,6 +58,48 @@ public void TestDefaultTnir(string dataSource, bool? tnirEnabledInConnString, Tr
Assert.Equal(expectedValue, connectionString.TransparentNetworkIPResolution);
}
#endif
+ ///
+ /// 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)
+ {
+ _appContextSwitchHelper.EnableMultiSubnetFailoverByDefaultField = msfEnabledAppContext;
+
+ SqlConnectionStringBuilder builder = new();
+ if (msfInConnString.HasValue)
+ {
+ builder.MultiSubnetFailover = msfInConnString.Value;
+ }
+ SqlConnectionString connectionString = new(builder.ConnectionString);
+
+ Assert.Equal(expectedValue, connectionString.MultiSubnetFailover);
+ }
+
+ ///
+ /// Tests that MultiSubnetFailover=true cannot be used with FailoverPartner.
+ ///
+ [Fact]
+ public void TestMultiSubnetFailoverWithFailoverPartnerThrows()
+ {
+ _appContextSwitchHelper.EnableMultiSubnetFailoverByDefaultField = Tristate.True;
+
+ SqlConnectionStringBuilder builder = new()
+ {
+ DataSource = "server",
+ FailoverPartner = "partner",
+ InitialCatalog = "database"
+ };
+
+ Assert.Throws(() => new SqlConnectionString(builder.ConnectionString));
+ }
public void Dispose()
{