Describe the bug
Microsoft.Data.SqlClient.Extensions.Azure 7.0.2 added a new
ActiveDirectoryAuthenticationProvider(ActiveDirectoryAuthenticationProviderOptions)
constructor. Microsoft.Data.SqlClient 7.0.1 cannot construct that type any more, so pairing
SqlClient 7.0.1 with Extensions.Azure 7.0.2 breaks all Microsoft Entra authentication.
Depending on the host, this surfaces in one of two ways:
- No Active Directory provider is installed —
SqlAuthenticationProvider.GetProvider(...)
returns null and connections using Entra authentication fail. Observed on Windows 11,
framework-dependent.
TypeInitializationException escapes the static constructor and every subsequent use of the
type fails. Observed on linux-x64 with a self-contained single-file publish (an EF Core migrations
bundle).
In 7.0.1 the manager instantiates the provider unconditionally:
// SqlAuthenticationProviderManager.cs (v7.0.1)
var instance = Activator.CreateInstance(
type,
[Instance._applicationClientId])
as SqlAuthenticationProvider;
When no applicationClientId is configured, that argument is null. A null argument is
assignable to both String and ActiveDirectoryAuthenticationProviderOptions, so the binder finds
two equally good candidates and throws AmbiguousMatchException.
The catch filter in 7.0.1 does not list AmbiguousMatchException, so it escapes the static
constructor. 7.0.2 fixed both halves of this (it selects the constructor explicitly and adds
AmbiguousMatchException to the filter), but nothing prevents the mismatched combination from being
resolved by NuGet — see "Further technical details" below for why this is easy to hit accidentally.
Exception message:
System.TypeInitializationException: The type initializer for
'Microsoft.Data.SqlClient.SqlAuthenticationProviderManager' threw an exception.
---> System.Reflection.AmbiguousMatchException: Ambiguous match found for
'Microsoft.Data.SqlClient.ActiveDirectoryAuthenticationProvider Void .ctor(System.String)'.
Stack trace:
at System.DefaultBinder.BindToMethod(BindingFlags bindingAttr, MethodBase[] match, Object[]& args, ParameterModifier[] modifiers, CultureInfo cultureInfo, String[] names, Object& state)
at System.RuntimeType.CreateInstanceImpl(BindingFlags bindingAttr, Binder binder, Object[] args, CultureInfo culture)
at Microsoft.Data.SqlClient.SqlAuthenticationProviderManager..cctor()
--- End of inner exception stack trace ---
at Microsoft.Data.SqlClient.SqlAuthenticationProviderManager.GetProvider(SqlAuthenticationMethod authenticationMethod)
at Microsoft.Data.SqlClient.Connection.SqlConnectionInternal.GetFedAuthToken(SqlFedAuthInfo fedAuthInfo)
at Microsoft.Data.SqlClient.Connection.SqlConnectionInternal.OnFedAuthInfo(SqlFedAuthInfo fedAuthInfo)
at Microsoft.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady)
at Microsoft.Data.SqlClient.Connection.SqlConnectionInternal.CompleteLogin(Boolean enlistOK)
at Microsoft.Data.SqlClient.Connection.SqlConnectionInternal.LoginNoFailover(...)
at Microsoft.Data.SqlClient.SqlConnection.Open(SqlConnectionOverrides overrides)
To reproduce
Complete, self-contained console project. No server is required — the exception happens while the
provider is being constructed.
Repro.csproj:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Data.SqlClient" Version="7.0.1" />
<PackageReference Include="Microsoft.Data.SqlClient.Extensions.Azure" Version="7.0.2" />
</ItemGroup>
</Project>
Program.cs:
using System.Reflection;
using Microsoft.Data.SqlClient;
var extensions = Assembly.Load("Microsoft.Data.SqlClient.Extensions.Azure");
var providerType = extensions.GetType("Microsoft.Data.SqlClient.ActiveDirectoryAuthenticationProvider")!;
Console.WriteLine("Constructors:");
foreach (var ctor in providerType.GetConstructors())
{
Console.WriteLine(" (" + string.Join(", ", ctor.GetParameters().Select(p => p.ParameterType.Name)) + ")");
}
// Exactly what SqlAuthenticationProviderManager's static constructor does in 7.0.1
// when no applicationClientId has been configured.
try
{
var instance = Activator.CreateInstance(providerType, new object?[] { null });
Console.WriteLine("Created: " + instance?.GetType().Name);
}
catch (Exception ex)
{
Console.WriteLine(ex.GetType().Name + ": " + ex.Message);
}
Output:
Constructors:
()
(String)
(Func`2, String)
(ActiveDirectoryAuthenticationProviderOptions)
AmbiguousMatchException: Ambiguous match found for
'Microsoft.Data.SqlClient.ActiveDirectoryAuthenticationProvider Void .ctor(System.String)'.
To see the real-world symptom instead, keep the same package versions and open a connection using
Authentication=Active Directory Default. The connection fails with the TypeInitializationException
above rather than an authentication error.
Expected behavior
One of the following:
Microsoft.Data.SqlClient.Extensions.Azure declares a lower bound on Microsoft.Data.SqlClient
(or the reverse), so NuGet cannot resolve an extension version newer than the core library that
understands it. Today neither package constrains the other — Extensions.Azure only depends on
Microsoft.Data.SqlClient.Extensions.Abstractions — so the mismatch resolves silently.
- Failing that, a clear, actionable exception naming the version mismatch, instead of a
TypeInitializationException wrapping a reflection binder error.
Adding the new (Options) constructor is source and binary compatible, but it is not compatible
with how older versions of the core library reflectively construct the type, so in practice it is a
breaking change for any already-resolved graph.
Further technical details
Microsoft.Data.SqlClient version: 7.0.1 (broken with Extensions.Azure 7.0.2); 7.0.2 is fine
Microsoft.Data.SqlClient.Extensions.Azure version: 7.0.2 (1.0.0 is fine with 7.0.1)
.NET target: .NET 10.0
SQL Server version: Azure SQL Database
Operating system: Ubuntu 24.04 (CI runner) and Windows 11
Constructor comparison:
| Extensions.Azure version |
Constructors on ActiveDirectoryAuthenticationProvider |
| 1.0.0 |
(), (String), (Func<DeviceCodeResult, Task>, String) |
| 7.0.2 |
(), (String), (Func<DeviceCodeResult, Task>, String), (ActiveDirectoryAuthenticationProviderOptions) |
| 7.1.0-preview2 |
same as 7.0.2 |
Version matrix observed:
Microsoft.Data.SqlClient |
Extensions.Azure |
Result |
| 7.0.1 |
1.0.0 |
works |
| 7.0.1 |
7.0.2 |
TypeInitializationException |
| 7.0.2 |
7.0.2 |
works |
Additional context
This is easy to hit unintentionally. An application only needs to reference
Microsoft.Data.SqlClient.Extensions.Azure explicitly (as the migration guidance instructs) while
picking up Microsoft.Data.SqlClient transitively. Microsoft.EntityFrameworkCore.SqlServer 10.0.10
requires Microsoft.Data.SqlClient 7.0.1, so a routine automated bump of the extension package to
7.0.2 silently produces the broken 7.0.1 + 7.0.2 pair, with no build warning.
The failure surfaced only at runtime, on the first connection using Microsoft Entra authentication.
Possibly related 7.0.2 changes around the new options/broker constructor: #4414, #4426.
Workaround
Reference Microsoft.Data.SqlClient explicitly and keep it version-aligned with
Microsoft.Data.SqlClient.Extensions.Azure:
<PackageReference Include="Microsoft.Data.SqlClient" Version="7.0.2" />
<PackageReference Include="Microsoft.Data.SqlClient.Extensions.Azure" Version="7.0.2" />
Describe the bug
Microsoft.Data.SqlClient.Extensions.Azure7.0.2 added a newActiveDirectoryAuthenticationProvider(ActiveDirectoryAuthenticationProviderOptions)constructor.
Microsoft.Data.SqlClient7.0.1 cannot construct that type any more, so pairingSqlClient 7.0.1 with Extensions.Azure 7.0.2 breaks all Microsoft Entra authentication.
Depending on the host, this surfaces in one of two ways:
SqlAuthenticationProvider.GetProvider(...)returns
nulland connections using Entra authentication fail. Observed on Windows 11,framework-dependent.
TypeInitializationExceptionescapes the static constructor and every subsequent use of thetype fails. Observed on linux-x64 with a self-contained single-file publish (an EF Core migrations
bundle).
In 7.0.1 the manager instantiates the provider unconditionally:
When no
applicationClientIdis configured, that argument isnull. Anullargument isassignable to both
StringandActiveDirectoryAuthenticationProviderOptions, so the binder findstwo equally good candidates and throws
AmbiguousMatchException.The
catchfilter in 7.0.1 does not listAmbiguousMatchException, so it escapes the staticconstructor. 7.0.2 fixed both halves of this (it selects the constructor explicitly and adds
AmbiguousMatchExceptionto the filter), but nothing prevents the mismatched combination from beingresolved by NuGet — see "Further technical details" below for why this is easy to hit accidentally.
To reproduce
Complete, self-contained console project. No server is required — the exception happens while the
provider is being constructed.
Repro.csproj:Program.cs:Output:
To see the real-world symptom instead, keep the same package versions and open a connection using
Authentication=Active Directory Default. The connection fails with theTypeInitializationExceptionabove rather than an authentication error.
Expected behavior
One of the following:
Microsoft.Data.SqlClient.Extensions.Azuredeclares a lower bound onMicrosoft.Data.SqlClient(or the reverse), so NuGet cannot resolve an extension version newer than the core library that
understands it. Today neither package constrains the other —
Extensions.Azureonly depends onMicrosoft.Data.SqlClient.Extensions.Abstractions— so the mismatch resolves silently.TypeInitializationExceptionwrapping a reflection binder error.Adding the new
(Options)constructor is source and binary compatible, but it is not compatiblewith how older versions of the core library reflectively construct the type, so in practice it is a
breaking change for any already-resolved graph.
Further technical details
Microsoft.Data.SqlClient version: 7.0.1 (broken with Extensions.Azure 7.0.2); 7.0.2 is fine
Microsoft.Data.SqlClient.Extensions.Azure version: 7.0.2 (1.0.0 is fine with 7.0.1)
.NET target: .NET 10.0
SQL Server version: Azure SQL Database
Operating system: Ubuntu 24.04 (CI runner) and Windows 11
Constructor comparison:
ActiveDirectoryAuthenticationProvider(),(String),(Func<DeviceCodeResult, Task>, String)(),(String),(Func<DeviceCodeResult, Task>, String),(ActiveDirectoryAuthenticationProviderOptions)Version matrix observed:
Microsoft.Data.SqlClientExtensions.AzureTypeInitializationExceptionAdditional context
This is easy to hit unintentionally. An application only needs to reference
Microsoft.Data.SqlClient.Extensions.Azureexplicitly (as the migration guidance instructs) whilepicking up
Microsoft.Data.SqlClienttransitively.Microsoft.EntityFrameworkCore.SqlServer10.0.10requires
Microsoft.Data.SqlClient7.0.1, so a routine automated bump of the extension package to7.0.2 silently produces the broken 7.0.1 + 7.0.2 pair, with no build warning.
The failure surfaced only at runtime, on the first connection using Microsoft Entra authentication.
Possibly related 7.0.2 changes around the new options/broker constructor: #4414, #4426.
Workaround
Reference
Microsoft.Data.SqlClientexplicitly and keep it version-aligned withMicrosoft.Data.SqlClient.Extensions.Azure: