dotnet / roslyn

The Roslyn .NET compiler provides C# and Visual Basic languages with rich code analysis APIs.
https://docs.microsoft.com/dotnet/csharp/roslyn-sdk/
MIT License
18.96k stars 4.02k forks source link

Microsoft.Data.SqlCient Could not load file or assembly 'System.Security.Principal.Windows' #51054

Open NealCallaghan opened 3 years ago

NealCallaghan commented 3 years ago

Version Used: dotnet 5.0 Windows

Steps to Reproduce:

  1. Follow these instructions on adding references https://github.com/dotnet/roslyn/discussions/47517#discussioncomment-64145
  2. Have a standard 2.0 class library project with the following xml
<Project Sdk="Microsoft.NET.Sdk">

    <PropertyGroup>
        <TargetFramework>netstandard2.0</TargetFramework>
        <LangVersion>9.0</LangVersion>
    </PropertyGroup>

    <ItemGroup>
        <PackageReference Include="Func" Version="0.2.2" />
        <PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.2">
            <PrivateAssets>all</PrivateAssets>
            <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
        </PackageReference>
        <PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="3.8.0" PrivateAssets="all"/>
    </ItemGroup>

  <ItemGroup>
    <!-- Generator dependencies -->
    <PackageReference Include="Microsoft.Data.SqlClient" Version="2.1.1" GeneratePathProperty="true" PrivateAssets="all" />
  </ItemGroup>

    <PropertyGroup>
        <GetTargetPathDependsOn>$(GetTargetPathDependsOn);GetDependencyTargetPaths</GetTargetPathDependsOn>
    </PropertyGroup>

    <Target Name="GetDependencyTargetPaths">
        <ItemGroup>
             <!--<TargetPathWithTargetPlatformMoniker Include="$(PKGSystem_Security_Principal_Windows)\runtimes\win\lib\netstandard1.3\System.Security.Principal.Windows.dll" IncludeRuntimeDependency="false" />-->
             <TargetPathWithTargetPlatformMoniker Include="$(PKGMicrosoft_Data_SqlClient)\runtimes\win\lib\netstandard2.0\Microsoft.Data.SqlClient.dll" IncludeRuntimeDependency="false" />
        </ItemGroup>
    </Target>

</Project>
  1. Reference the library from a console application which is net5.0
  2. Insert a line Debugger.Launch(); within a generator Execute method
  3. create a try catch
  4. within the try catch attempt to create a sqlconnection and open it.
_dbConnection = new SqlConnection();           
_dbConnection.ConnectionString = @"validconnectionstring";
_dbConnection.Open(); //exception thrown here

Expected Behavior: All the dependencies for SqlConnection should have been loaded and the connection should be able to open.

Actual Behavior: Exception thrown with message:

Could not load file or assembly 'System.Security.Principal.Windows, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies. The system cannot find the file specified.

Source: Microsoft.Data.SqlClient

Stacktrace: at Microsoft.Data.ProviderBase.DbConnectionPoolIdentity.GetCurrentNative() 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, TaskCompletionSource1 retry, DbConnectionOptions userOptions, DbConnectionInternal oldConnection, DbConnectionInternal& connection) at Microsoft.Data.ProviderBase.DbConnectionInternal.TryOpenConnectionInternal(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource1 retry, DbConnectionOptions userOptions) at Microsoft.Data.SqlClient.SqlConnection.TryOpen(TaskCompletionSource`1 retry, SqlConnectionOverrides overrides) at Microsoft.Data.SqlClient.SqlConnection.Open(SqlConnectionOverrides overrides) at CSharp.Data.Sql.Schema.Provider.SqlServer.DbConnectionAsync..ctor() in C:\Dev\CSharpSQLProvider\SQLProvider\CSharp.Data.Sql\Schema\Provider\SqlServer\DbConnectionAsync.cs:line 18 at CSharp.Data.Sql.Generator.DataContextGenerator.Execute(GeneratorExecutionContext context) in C:\Dev\CSharpSQLProvider\SQLProvider\CSharp.Data.Sql\Generator\DataContextGenerator.cs:line 57

Innerexception is null.

To be clear, I am able to create an instance of SqlConnection just when setting the connection string this error is thrown. I also tried the System.Data.SqlClient, however I wasn't able to create a SqlConnection instance but the same exception was thrown just with a different assembly name.

I have also tried adding the dependency into the csproj in the same way as I have for Microsoft.Data.SqlClient in the example given to no avail, also attempted to load the required assemblies into the app domain, and also tried adding the assembly into the context compilation.

var assembly = Assembly.LoadFrom(@"C:\Users\Neal Callaghan\.nuget\packages\system.security.principal.windows\4.4.1\runtimes\win\lib\netcoreapp2.0\System.Security.Principal.Windows.dll");

 var refe = MetadataReference.CreateFromFile(assembly.Location);
 var foo = context.Compilation.ExternalReferences.Add(refe);
var d = context.Compilation.AddReferences(new[] { refe });

I suspect I'm doing something wrong and any help would be appreciated. Thank you.

I should also mention that the missing assembly is apart of the returned assemblies in AppDomain.CurrentDomain.GetAssemblies() within the execute method

dotnet-issue-labeler[bot] commented 3 years ago

I couldn't figure out the best area label to add to this issue. If you have write-permissions please help me learn by adding exactly one area label.

NealCallaghan commented 3 years ago

I believe this is related to https://github.com/IntelliTect/AspNetCore.TestHost.WindowsAuth/issues/9 and https://github.com/dotnet/runtime/issues/28161

NealCallaghan commented 3 years ago

I have gone back to the samples provided here https://github.com/dotnet/roslyn-sdk/tree/master/samples/CSharp/SourceGenerators and reproduced the same issue

sharwell commented 3 years ago

Assembly.LoadFrom

Source generators and analyzers must not load assemblies directly. So the first step will be finding a way to avoid trying to do this.

NealCallaghan commented 3 years ago

Assembly.LoadFrom

Source generators and analyzers must not load assemblies directly. So the first step will be finding a way to avoid trying to do this.

Yes this me experimenting trying to get to the bottom of the issue.

Based on the stack trace I looked into the Microsoft.Data.SqlClient source. Ultimately the code that is throwing is this below

using System;
using System.Security.Principal;
using Microsoft.Data.SqlClient;

namespace Microsoft.Data.ProviderBase
{
    partial class DbConnectionPoolIdentity
    {
        private static DbConnectionPoolIdentity s_lastIdentity = null;

        internal static DbConnectionPoolIdentity GetCurrent()
        {
            return TdsParserStateObjectFactory.UseManagedSNI ? GetCurrentManaged() : GetCurrentNative();
        }

        private static DbConnectionPoolIdentity GetCurrentNative()
        {
            DbConnectionPoolIdentity current;
            using (WindowsIdentity identity = WindowsIdentity.GetCurrent())
            {
                IntPtr token = identity.AccessToken.DangerousGetHandle();
                SecurityIdentifier user = identity.User;
                bool isNetwork = user.IsWellKnown(WellKnownSidType.NetworkSid);
                string sidString = user.Value;

                // Win32NativeMethods.IsTokenRestricted will raise exception if the native call fails
                bool isRestricted = Win32NativeMethods.IsTokenRestrictedWrapper(token);

                var lastIdentity = s_lastIdentity;
                if ((lastIdentity != null) && (lastIdentity._sidString == sidString) && (lastIdentity._isRestricted == isRestricted) && (lastIdentity._isNetwork == isNetwork))
                {
                    current = lastIdentity;
                }
                else
                {
                    current = new DbConnectionPoolIdentity(sidString, isRestricted, isNetwork);
                }
            }
            s_lastIdentity = current;
            return current;
        }
    }
}

@sharwell If I had to guess there is an issue with WindowsIdentity.GetCurrent() (System.Security.Principal.WindowsIdentity). I have a feeling there is an issue with a code generator accessing this data, maybe something to do with application domain security within generators I'm not aware of?

NealCallaghan commented 3 years ago

@sharwell here is a link to the source file in sqlclient where the exception is thrown https://github.com/dotnet/SqlClient/blob/master/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/ProviderBase/DbConnectionPoolIdentity.Windows.cs

NealCallaghan commented 3 years ago

@chsienki is this a bug? Or am I doing something wrong in importing packages? Thank you for any information.

NealCallaghan commented 3 years ago

I am able to open a connection with the suggestion from here https://github.com/dotnet/roslyn/issues/49462 however I would need to test more methods.

rosian22 commented 2 years ago

Hello, in my case switching from Microsoft.Data.SqlClient to System.Data.SqlClient fixed the issue. I had a similar problem inside an azure function time triggered in which I was attempting an SqlConnection