Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ private enum Tristate : byte
internal const string LegacyVarTimeZeroScaleBehaviourString = @"Switch.Microsoft.Data.SqlClient.LegacyVarTimeZeroScaleBehaviour";
internal const string UseConnectionPoolV2String = @"Switch.Microsoft.Data.SqlClient.UseConnectionPoolV2";
private const string IgnoreServerProvidedFailoverPartnerString = @"Switch.Microsoft.Data.SqlClient.IgnoreServerProvidedFailoverPartner";
private const string EnableMultiSubnetFailoverByDefaultString = @"Switch.Microsoft.Data.SqlClient.EnableMultiSubnetFailoverByDefault";

// 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;
Expand All @@ -32,6 +33,7 @@ private enum Tristate : byte
private static Tristate s_legacyVarTimeZeroScaleBehaviour;
private static Tristate s_useConnectionPoolV2;
private static Tristate s_ignoreServerProvidedFailoverPartner;
private static Tristate s_multiSubnetFailoverByDefault;


#if NET
Expand Down Expand Up @@ -263,5 +265,30 @@ public static bool IgnoreServerProvidedFailoverPartner
return s_ignoreServerProvidedFailoverPartner == Tristate.True;
}
}

/// <summary>
/// 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'.
/// </summary>
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;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ internal static class DEFAULT
internal const bool MARS = DbConnectionStringDefaults.MultipleActiveResultSets;
internal const int Max_Pool_Size = DbConnectionStringDefaults.MaxPoolSize;
internal const int Min_Pool_Size = DbConnectionStringDefaults.MinPoolSize;
internal const bool MultiSubnetFailover = DbConnectionStringDefaults.MultiSubnetFailover;
internal static bool MultiSubnetFailover => DbConnectionStringDefaults.MultiSubnetFailover;
internal const int Packet_Size = DbConnectionStringDefaults.PacketSize;
internal const string Password = DbConnectionStringDefaults.Password;
internal const bool Persist_Security_Info = DbConnectionStringDefaults.PersistSecurityInfo;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ public sealed class LocalAppContextSwitchesHelper : IDisposable
#if NETFRAMEWORK
private readonly PropertyInfo _disableTnirByDefaultProperty;
#endif
private readonly PropertyInfo _enableMultiSubnetFailoverByDefaultProperty;

// These fields are used to capture the original switch values.
private readonly FieldInfo _legacyRowVersionNullBehaviorField;
Expand All @@ -48,10 +49,13 @@ public sealed class LocalAppContextSwitchesHelper : IDisposable
private readonly Tristate _useConnectionPoolV2Original;
private readonly FieldInfo _ignoreServerProvidedFailoverPartnerField;
private readonly Tristate _ignoreServerProvidedFailoverPartnerOriginal;
#if NETFRAMEWORK
#if NETFRAMEWORK
private readonly FieldInfo _disableTnirByDefaultField;
private readonly Tristate _disableTnirByDefaultOriginal;
#endif
#endif
private readonly FieldInfo _multiSubnetFailoverByDefaultField;
private readonly Tristate _multiSubnetFailoverByDefaultOriginal;


#endregion

Expand Down Expand Up @@ -139,6 +143,10 @@ void InitProperty(string name, out PropertyInfo property)
out _disableTnirByDefaultProperty);
#endif

InitProperty(
"EnableMultiSubnetFailoverByDefault",
out _enableMultiSubnetFailoverByDefaultProperty);

// A local helper to capture the original value of a switch.
void InitField(string name, out FieldInfo field, out Tristate value)
{
Expand Down Expand Up @@ -195,6 +203,11 @@ void InitField(string name, out FieldInfo field, out Tristate value)
out _disableTnirByDefaultField,
out _disableTnirByDefaultOriginal);
#endif

InitField(
"s_multiSubnetFailoverByDefault",
out _multiSubnetFailoverByDefaultField,
out _multiSubnetFailoverByDefaultOriginal);
}

/// <summary>
Expand Down Expand Up @@ -255,6 +268,10 @@ void RestoreField(FieldInfo field, Tristate value)
_disableTnirByDefaultOriginal);
#endif

RestoreField(
_multiSubnetFailoverByDefaultField,
_multiSubnetFailoverByDefaultOriginal);

if (failedFields.Count > 0)
{
throw new Exception(
Expand Down Expand Up @@ -327,6 +344,11 @@ public bool DisableTnirByDefault
}
#endif

public bool EnableMultiSubnetFailoverByDefault
{
get => (bool)_enableMultiSubnetFailoverByDefaultProperty.GetValue(null);
}

// 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.
Expand Down Expand Up @@ -408,6 +430,12 @@ public Tristate DisableTnirByDefaultField
}
#endif

public Tristate EnableMultiSubnetFailoverByDefaultField
{
get => GetValue(_multiSubnetFailoverByDefaultField);
set => SetValue(_multiSubnetFailoverByDefaultField, value);
}

#endregion

#region Private Helpers
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ public class LocalAppContextSwitchesTests
#if NETFRAMEWORK
[InlineData("DisableTnirByDefault", false)]
#endif
[InlineData("EnableMultiSubnetFailoverByDefault", false)]
public void DefaultSwitchValue(string property, bool expectedDefaultValue)
{
var switchesType = typeof(SqlCommand).Assembly.GetType("Microsoft.Data.SqlClient.LocalAppContextSwitches");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public SqlConnectionStringTest()
_appContextSwitchHelper = new LocalAppContextSwitchesHelper();
}

#if NETFRAMEWORK
#if NETFRAMEWORK
[Theory]
[InlineData("test.database.windows.net", true, Tristate.True, true)]
[InlineData("test.database.windows.net", false, Tristate.True, false)]
Expand Down Expand Up @@ -60,7 +60,50 @@ public void TestDefaultTnir(string dataSource, bool? tnirEnabledInConnString, Tr
// Assert
Assert.Equal(expectedValue, connectionString.TransparentNetworkIPResolution);
}
#endif
#endif

/// <summary>
/// Test MSF values when set through connection string and through app context switch.
/// </summary>
[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);
}

/// <summary>
/// Tests that MultiSubnetFailover=true cannot be used with FailoverPartner.
/// </summary>
[Fact]
public void TestMultiSubnetFailoverWithFailoverPartnerThrows()
{
_appContextSwitchHelper.EnableMultiSubnetFailoverByDefaultField = Tristate.True;

SqlConnectionStringBuilder builder = new()
{
DataSource = "server",
FailoverPartner = "partner",
InitialCatalog = "database"
};

Assert.Throws<ArgumentException>(() => new SqlConnectionString(builder.ConnectionString));
}

public void Dispose()
{
Expand Down
Loading