Description
The Microsoft.Data.SqlClient package, as of version 1.0.19189.1-Preview
, carries MSBuild targets that need to run when building .NET Framework apps in order to copy sni.dll
. However, these targets are not "transitive" which means if you reference SqlClient indirectly through a library, you don't get those build targets and end up with errors.
It's my understanding that this can be solved by using a buildTransitive/
folder instead of build/
. See the NuGet spec on the subject for more info. This was released in NuGet 5.0, so it does mean the package will require NuGet 5.0 or higher if it uses this feature.
Users can work around this issue by adding a direct dependency to Microsoft.Data.SqlClient
but this is not the standard or expected practice in projects using the modern <PackageReference>
syntax, which we do support for .NET Framework packages.
Repro steps
Example repo: https://github.com/anurse/SqlClientNotTransitive
- Clone the repo
dotnet restore
dotnet run --project ./Consumer
Detailed steps (from scratch instead of using the repo)
cd
to some empty directorydotnet new sln
dotnet new console --name Consumer
dotnet sln add ./Consumer
dotnet new classlib --name Library
dotnet sln add ./Library
- Add a
<PackageReference>
toMicrosoft.Data.SqlClient 1.0.19189.1-Preview
inLibrary
. - Add a
<ProjectReference>
toLibrary
inConsumer
- Change the
Consumer.csproj
to targetnet471
- Add
Connector.cs
toLibrary
:
using System;
using Microsoft.Data.SqlClient;
namespace Library
{
public static class Connector
{
public static void Connect()
{
var connection = new SqlConnection(@"Server=(localdb)\MSSQLLocalDB;Database=CacheTestDb;Trusted_Connection=True;");
connection.Open();
}
}
}
- Change
Program.cs
inConsumer
to:
using System;
using Library;
namespace Consumer
{
class Program
{
static void Main(string[] args)
{
Connector.Connect();
}
}
}
dotnet run --project ./Consumer
Expected Behavior
Connecting
Connected
This presumes the connection string above is valid for your environment. Even if it isn't you should at least see a connection failure message instead of what you end up getting.
Actual Behavior
Connecting
Unhandled Exception: System.TypeInitializationException: The type initializer for 'Microsoft.Data.SqlClient.SNINativeMethodWrapper' threw an exception. ---> System.ComponentModel.Win32Exception: Failed to load C:\Code\anurse\SqlClientNotTransitive\Consumer\bin\Debug\net471\x64\SNI.dll
at Microsoft.Data.SqlClient.SNINativeMethodWrapper..cctor()
--- End of inner exception stack trace ---
at Microsoft.Data.SqlClient.SNINativeMethodWrapper.UnmanagedIsTokenRestricted(IntPtr token, Boolean& isRestricted)
at Microsoft.Data.Win32NativeMethods.IsTokenRestrictedWrapper(IntPtr token)
at Microsoft.Data.ProviderBase.DbConnectionPoolIdentity.GetCurrent()
at Microsoft.Data.ProviderBase.DbConnectionPoolGroup.GetConnectionPool(DbConnectionFactory connectionFactory)
at Microsoft.Data.ProviderBase.DbConnectionFactory.GetConnectionPool(DbConnection owningObject, DbConnectionPoolGroup connectionPoolGroup)
at Microsoft.Data.ProviderBase.DbConnectionFactory.TryGetConnection(DbConnection owningConnection, TaskCompletionSource`1 retry, DbConnectionOptions userOptions, DbConnectionInternal oldConnection, DbConnectionInternal& connection)
at Microsoft.Data.ProviderBase.DbConnectionInternal.TryOpenConnectionInternal(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource`1 retry, DbConnectionOptions userOptions)
at Microsoft.Data.SqlClient.SqlConnection.TryOpenInner(TaskCompletionSource`1 retry)
at Microsoft.Data.SqlClient.SqlConnection.TryOpen(TaskCompletionSource`1 retry)
at Microsoft.Data.SqlClient.SqlConnection.Open()
at Library.Connector.Connect()
at Consumer.Program.Main(String[] args)
The build output from Consumer
does indeed show no sni.dll
:
> dir .\Consumer\bin\Debug\net472
Directory: C:\Code\anurse\SqlClientNotTransitive\Consumer\bin\Debug\net471
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 7/10/2019 1:24 PM 4608 Consumer.exe
-a---- 7/10/2019 1:18 PM 4264 Consumer.exe.config
-a---- 7/10/2019 1:24 PM 468 Consumer.pdb
-a---- 7/10/2019 1:24 PM 4096 Library.dll
-a---- 7/10/2019 1:24 PM 524 Library.pdb
-a---- 7/8/2019 4:59 PM 1880016 Microsoft.Data.SqlClient.dll
-a---- 12/11/2017 10:10 AM 146616 System.Data.Common.dll
-a---- 12/11/2017 10:10 AM 23264 System.Diagnostics.StackTrace.dll
-a---- 12/11/2017 10:10 AM 31448 System.Diagnostics.Tracing.dll
-a---- 12/11/2017 10:10 AM 24296 System.Globalization.Extensions.dll
-a---- 12/11/2017 10:10 AM 110784 System.IO.Compression.dll
-a---- 12/11/2017 10:10 AM 198464 System.Net.Http.dll
-a---- 12/11/2017 10:10 AM 23224 System.Net.Sockets.dll
-a---- 12/11/2017 10:10 AM 26888 System.Runtime.Serialization.Primitives.dll
-a---- 12/11/2017 10:10 AM 44816 System.Security.Cryptography.Algorithms.dll
-a---- 12/11/2017 10:10 AM 22240 System.Security.SecureString.dll
-a---- 12/11/2017 10:10 AM 25816 System.Threading.Overlapped.dll
-a---- 12/11/2017 10:10 AM 22744 System.Xml.XPath.XDocument.dll