System.Data.SqlClient does not work when loading the external library (.NET Standard DLL) at runtime through reflection except if the library has been published for a specific platform (rid).
Trying to execute a SQL query against a SQL Server instance throws "System.PlatformNotSupportedException: System.Data.SqlClient is not supported on this platform" exception.
Repro steps and Visual studio solution can be found here:
https://github.com/softeering/NETStandardSQLClientRepro
I'm pretty sure this can be explained but I don't get why it doesn't work
I have a the same error runing a different code set. The erorr is thown on the SqlConnection action.
The SqlClient package contains a "default" implementation for OS other than Unix/Windows which as you found throws an exception indicating it is dummy - does not work.
What are you trying to achieve? Can you load the Windows or Unix one instead?
I'm trying to load a .NET Standard external assembly (one I wrote) via reflection. This assembly depends on System.Data.SqlClient but when I try to open a SqlConnection, the exception above is thrown.
FYI:
As a reminder, deeper explanation, repro steps and Visual studio solution can be found here:
https://github.com/softeering/NETStandardSQLClientRepro
Thank you for the repro. I see the no-rid publish gives
NETStandardSQLClientRepro\ConsoleApp1\output\ClassLibrary1.Implementation.dll
NETStandardSQLClientRepro\ConsoleApp1\output\System.Data.SqlClient.dll
NETStandardSQLClientRepro\ConsoleApp1\output\runtimes\unix\lib\netstandard2.0\System.Data.SqlClient.dll
NETStandardSQLClientRepro\ConsoleApp1\output\runtimes\win\lib\netstandard2.0\System.Data.SqlClient.dll
The SqlClient next to your app is of course the throwing one. So your question is how to load and execute code in ClassLibrary1.Implementation.dll in such a way that it knows to load runtimes\win\lib\netstandard2.0\System.Data.SqlClient.dll instead of the System.Data.SqlClient.dll right next to it.
@weshaggard can you answer that? I am not famliar yet with .NET Core loader.
Yes it seems to be the issue.
The wrong System.Data.SqlClient.dll gets loaded when ClassLibrary1.Implementation.dll is loaded via reflection from the Console app (and works when ClassLibrary1.Implementation.dll is directly referenced in the console app)
FYI, if you delete the System.Data.SqlClient.dll under the root folder, the console app is not able to automatically load the one under the "runtimes" folder. You then get this error:
System.IO.FileNotFoundException: 'Could not load file or assembly 'System.Data.SqlClient, Version=4.2.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'. The system cannot find the file specified.'
Is there a reason you are creating your own custom AssemblyLoaderContext? When you create your own context you are responsible for finding and loading the correct assembly yourself. In your case line https://github.com/softeering/NETStandardSQLClientRepro/blob/master/ConsoleApp1/ConsoleApp1/Program.cs#L51 is trying to load an assembly by the same name in the root folder so when you delete it that is why it fails.
If you want the runtime to do the work for you then you will need to reference System.Data.SqlClient package from the application project so that it ends up in the deps file and thus will give the correct information to the loader of which one to load. Otherwise the custom AssemblyLoadContext is the one responsible for finding the correct version to load.
So depending on your scenario I would suggest you reference System.Data.SqlClient from your application and just call AssemblyLoadContext.Default.LoadFromAssembyPath to dynamically load your class library.
In my case I'm trying to create a DB provider, for an application, so it can use multiple databases.
And I am using:
var myAssembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(
However I have a 3 level architecture and I do call this from the 2nd level, the 3rd level being the actual provider which calls the connection method from System.Data.Client. So your explanation of the wrong context makes sense.
I only have test code, to try out my ideas in .Net Core. But I'll move it up to GitHub so everyone can see it.
I'll try the assembly load in the provider, to see if I can get it to work.
I'm using this code to initialize the provider...
public override void Connect(string XmlConfig)
{
var nbi = new NBrightInfo();
nbi.XmlString = XmlConfig;
foreach (var nod in nbi.XMLDoc.XPathSelectElements("genxml/dependancy/*"))
{
AssemblyLoadContext.Default.LoadFromAssemblyPath(nod.Value);
}
var myAssembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(nbi.GetXmlProperty("genxml/provider/assembly"));
var myType = myAssembly.GetType(nbi.GetXmlProperty("genxml/provider/namespaceclass"));
_objProvider = (IDataBaseInterface)Activator.CreateInstance(myType);
_objProvider.Connect(XmlConfig);
}
This works well with the DBreeze database, but he SQL connect fails.
The question is, I seem to be loading the provider and dependencies into the same "Default" context?
So why is it not taking the correct assembly?
Obviously adding a ref to SQL Client in the creation/calling application, defeats the idea of having a provider structure. But is this the reason it fails?
To me this seems a normal thing to do when creating a provider for SQL. So I would have expected this to work.
I'm not familiar with .Net Core so I'm happy to take any advice anyone has.
@weshaggard you're right, reason was simply a test for me. It didn't work with the default loader so I tried creating my own. I now understand where the problem could be.
I'll work to improve the loader in order to read other deps files and potentially visit the folder to find the right assembly since adding a reference to it is not a solution for me.
Thanks for the insight
If you cannot or do not want to have the application reference these packages then you will need to include a deps (or a deps like file) with your provider that you are loading dynamically and use that information in your custom AssemblyLoadContext to load the correct assembly. The default AssemblyLoadContext will only be able to able to find and load assemblies that are in the applications deps file.
The question seems to be answered, can we close the issue? (closing proactively)
Or is there some remaining question still open? (feel free to reopen / comment in such case)
@karelz More or less answered yes.
Would be great if you can direct me to some examples on how to use the DependencyContextJsonReader class to then load dependencies at runtime based on the platform the code is running on
Thanks
OK, I looked at the nugget packages and found, even though I used nugget to get the latest version, I assumed it was not using the latest version. (Or maybe not in some required context) So moved this dll to my "lib" folder :
.nugetpackagessystem.data.sqlclient4.4.0runtimeswinlibnetstandard2.0
I then used this dll in the lib folder as the dependency dll, I then got a new error, so I assume that the problem was solved by forcing the correct dll to be inject via the Assembly load.
Unfortunately can't get sni.dll to load with AssemblyLoadContext. Even when updating allnuget packages and trying all dll. But I suppose that's another issue.
@karelz Any feedback on my last question?
Do you have any example of how to write a custom AssemblyLoadContext that uses a DependencyContextJsonReader to manually load external references?
Thanks
@softeering I don't know.
Moreover I don't understand how it is related to this issue. Maybe StackOverflow or place where DependencyContextJsonReader lives (ASP.NET repos?) would be better place for the question ...
I can't help but feel this issue has been closed just to make statistics better. Although an explanation has been given, which sounds right. Neither of us have managed to get the SQL Client working using AssemblyLoadContext.
I think @softeering request for and example to prove that .Net Core work is relevant, because it still doesn't work!!
In my case I can make the system work if I place the nuget dependency on the calling application, but not if it's on the provider project. Which is not right. Surely having any provider based architecture should not demand that all references for all providers must be in the calling application. It makes very little sense for that to be correct.
I do have the same project working when I connect to a DBreeze database, but I cannot get the SQL to work with a provider based system.
Maybe this isn't the same issue as mentioned, or maybe the context is the problem, but I don't see any proof of that. Not being able to connect to SQL with a provider based project is a fairly important problem for .Net Core.
And as @softeering says, a simply example how this should work would prove there is not an issue and help everyone using .Net Core.
I've just realized my comments seems a little harsh. Sorry, I don't mean to be. I'm grateful for any help you can give.
+1 on the fact that we don't have a solution.
I understand that this is kind of specific but I used to load "plugins" via reflection in .NET 4.x and it worked well.
I'm now stuck with the migration to .NET Core / Standard 2.0 because I cannot manage to load all dependencies at runtime for the current platform (i.e. based on the RID)
We discussed it with @weshaggard. We still don't fully understand all plugin/provider model requirements in this case. Can someone please describe the requirements, limitations and design choices / their motivation?
The truth is that .NET Core assumes that all app dependencies are known at compile time (which they are also on .NET 4.x) and listed in deps.json file (which is done automatically only for static dependencies of the app).
Such model brings huge simplification and sanity to loader. It has some drawbacks we might address in future versions.
It is quite possible that using provider/plugin model is one such drawback and we need to come up either with guidance how to avoid it, or with changes to .NET Core to enable it.
We should not hang up on "it worked on Desktop, it has to work in .NET Core" just yet -- we may get to it, but we need to first understand what we are achieving with such thing and why.
On technical/sanity side, I would recommend to start the discussion in a new more general issue once we have common understanding of the question.
cc @jkotas @RussKeldorph
OK, Thanks for getting back to us on this.
In my case their is a concept that I want my application to support multiple Databases.
The standard method of this was to use a provider method, so we have an assembly for each database and at runtime/startup with load the required provider.
In my case I have this working with the DBreeze database. Which I must say has a simple dependency model.
However when I create the same code, but using the SQL Client dependency I can't get it to connect to SQL.
If I place a nugget package for SQL.Client in my calling application (not the provider) I CAN get the application to connect. But obviously doing that goes against my planned architecture.
In this case the wrong context makes sense to me, I'm loading the dependencies in the provider and it seems that it is not loaded into the application context and therefore I presume it cannot be used by the application?
It could be a simple case that I'm not loading all the dependencies for SQL Client, which I think is the case, but when I look at the dependencies in the deps.json file I see which are needed but have no idea how to find the full path and load them. I've tried to guess the correct dependencies, but don't seem able to find them all.
So if I had an example of how the SQL Client should be loaded at runtime, this may well solve my problem.
I've previously added a link to my code, it's a basic test app and simple, so should be easily understandable to see my problem.
Dependency injection is not an area I have much knowledge with. So please feel free to educate me!
Thanks for any help you can give.
@karelz while being specific and not common, motivation is quite simple: be able to load assemblies at runtime.
Let's say I have a .NET Core 2 console application which has some core services.
I would like this application to load external custom libraries (.NET Standard 2 "plugins") at runtime (load X.dll from folder Y).
This could for example be a custom implementation of a contract in my core project.
I used to hook up to the following event to ensure all assemblies get found (search for the specific assembly in the "plugins" folder for instance):
AppDomain.CurrentDomain.AssemblyResolve
Now with .NET Core 2, we should use AssemblyLoadContext. This works great if your "plugin" assembly:
However, let's take the example of a plugin requiring "System.Data.SqlClient.dll". This dll:
If you publish the plugin without a specific RID, you'll get a folder called "runtimes" with many sub-folder (one for each implemented platform).
AssemblyLoadContext is not able to resolve dependencies of my plugin assembly at runtime and it's hard to build a custom one which needs to handle RIDs correctly as well as unmanaged dlls.
I managed to write one which seems to work for my use-case but the code ends up being ugly and I'm pretty sure there is a better way of handling this.
As an additional requirement, I'd like to be able to use the custom AssemblyLoadContext to load dependencies while compiling and running the code at runtime through the Roslyn scripting API (i.e. didn't find a way to replace the one used by the CsharpScript roslyn API)
Repro VS solution can still be found here:
https://github.com/softeering/NETStandardSQLClientRepro
AppDomain.CurrentDomain.AssemblyResolve
You can still hookup this event in .NET Core 2 if it works for you. You do not have to use AssemblyLoadContext.
I think the problem here is, like softeering has point out. The dependacy model for System.Data.SqlClient is next to impossible to work out what needs loading.
Obviously the nuGet package is doing something when it's loaded into the assembly, becuase that works when we add the reference to the main application, without using AssemblyLoadContext.
What we're asking is for an example of how SqlClient depenacy injection works, becuase it seems imposible. And that raises the question, is it possible to do that? or is this a bug in .Net Core?
@jkotas Agree on the fact that AppDomain.CurrentDomain.AssemblyResolve could still be used but it's not getting called for unmanaged DLLs.
If I attach to this, the delegate gets called for System.Data.SqlClient library but not for sni.dll.
Any hint / input is welcome.
I agree that we have a feature gap around resolving unmanaged DLLs. I have commented on it here: https://github.com/dotnet/coreclr/issues/12707#issuecomment-332892829
@jkotas I'd be totally good with an answer like "this cannot work on .NET Core"... but yes, I'd be frustrated ;-)
I'd then need to stick to .NET and that's not the idea I had in mind
Implementing my own AssemblyLoadContext works but is really ugly (string-based RID) and I didn't manage to use this with the Roslyn compiler API
Thanks for your input
Yes, customizing loading of unmanaged dependencies is not easy in neither .NET Core or .NET Framework today.
The difference for you is that SqlClient is inbox on .NET Framework, but independent nuget package on .NET Core. If you try with a component that is independent nuget package on both .NET Core and .NET Framework, you will hit the same problems on both.
Yes, same issue. Am dynamically loading dll plugins via Autofac for a number of different data connectors. For the first time we have requirement to publish data into SQL Server and the System.Data.SqlClient won't load dynamically. So I guess for now load the into the controlling service that selects the correct plugin to use at service startup. This works around the issue in a rather clunky way but all the plugins are under our own control (for now) so while not ideal its workable.
Any update?
Any update? I Use .Net Core 3.0 AssemblyDependencyResolver to resolve a plugin. the plugin use dapper.
Same problem happens.
The issue is still replicable. I was using System.Data.SqlClient for a small POC and got error is not supported on this platform. . I later replaced this with Microsoft.Data.SqlClient and still getting Microsoft.Data.SqlClient is not supported on this platform. Need to know what can be done to remove this error.
@rajanbhayana we do not monitor closed issues. If there is a problem with a repro, please file it on https://github.com/dotnet/sqlclient repo. Thanks!
I've ran into this issue and problem only existed when using IIS, if I executed the application without directly it was fine. It turned out to be SNI was missing a dependency:
https://support.microsoft.com/en-us/help/2977003/the-latest-supported-visual-c-downloads
So I installed vc_redist.x64.exe and vc_redist.x86.exe and then it worked in IIS as well.
Hi this may not solve use cases for multiple databases, but I was able to get my .Net3.1 console app to finally work with SQLCLient by adding my AppName.deps.json to the install directory where you run your app from that uses SqlClient
Microsoft.Data.SqlClient works fine on my core 3.1 api project. My api requests and responses internally use this library and it works with no issues.
However, ...referencing that same (data access) assembly from my core api to a test project (MSTest/NUnit) is where I'm getting 'Microsoft.Data.SqlClient is not supported on this platform.' this error message.
Most helpful comment
+1 on the fact that we don't have a solution.
I understand that this is kind of specific but I used to load "plugins" via reflection in .NET 4.x and it worked well.
I'm now stuck with the migration to .NET Core / Standard 2.0 because I cannot manage to load all dependencies at runtime for the current platform (i.e. based on the RID)