natemcmaster / DotNetCorePlugins

.NET Core library for dynamically loading code
Apache License 2.0
1.59k stars 227 forks source link

Could not load System.Data.SqlClient? #141

Closed ShankarBUS closed 1 year ago

ShankarBUS commented 4 years ago

Describe the bug I'm creating an application which will load plugins from a directory (.\Extensions) in .NET Core 3.1 that's why I chose this library. Previously (in .NET 4.6.2) I was using AppDomain.AssemblyResolve to resolve dependencies and share some assemblies with the plugins. More details below.

My project structure is basically this :

The plugins will reference the core library and the UI library (in some cases). The main host app will resolve dependencies of the plugins using AppDomain.AssemblyResolve and share the core logic and UI libs with them.

This method still works. But I want features of this library such Unloading, HotReload, etc. So I switched to use this library and changed the code correspondingly.

But the problem is all 3 libraries are WPF .NET Core 3.1 libraries

The Core logic library depends on System.Drawing and the UI library depends on a 3rd party library (ModernWpf)

So to overcome the isolation boundry I have to share the types common to them like this :

private static PluginLoader GetDefaultLoader(string dllloc)
{
    return PluginLoader.CreateFromAssemblyFile(
                dllloc,
                isUnloadable: true,
                sharedTypes: new Type[]
                {
                    typeof(Core.ExtensionBase),
                    typeof(UI.BuskBar.IBuskBarItem),
                    typeof(ModernWpf.ApplicationTheme),
                    typeof(ModernWpf.Controls.AppBarButton),
                    typeof(System.Drawing.Image),
                });
}

I expected it to work like in .NET 4.6.2 (using AppDomain.AssemblyResolve) but it doesn't. It throws this System.IO.FileNotFoundException saying

"Could not load file or assembly 'System.Data.SqlClient, Version=4.6.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'. The system cannot find the file specified."

None of my projects/their dependencies depend on System.Data.SqlClient. I checked all of their *.deps.json. What should I do?

To Reproduce Steps to reproduce the behavior: I'm on a tight schedule and I can't provide any repro of it but basically

  1. Create 2 WPF class library projects (one .Core -> depends on System.Drawing.Common and one .UI -> depends on ModernWpf).
  2. Reference those 2 in a new WPF Application project.
  3. Create a skeletal structure of my project -> a abstract class ExtensionBase in the .Core project and a interface IBuskBarItem in .UI project.
  4. Create a basic WPF class library project called MyPlugin reference the UI and Core project with copy local set to false.
  5. Try to load the plugin assembly (MyPlugin.dll) using the afore-mentioned GetDefaultLoader(path_to_plugin).
  6. You will get a System.IO.FileNotFoundException like I mentioned above.

Expected behavior I don't have any dependencies to System.Data.SqlClient. I want mine to work like it did in .NET 4.6.2

Screenshots image

Additional context I even tried to remove the .Core and .UI references from the plugin and modified the loading as

private static PluginLoader GetDefaultLoader(string dllloc)
{
    return PluginLoader.CreateFromAssemblyFile(
                dllloc,
                isUnloadable: true,
                sharedTypes: new Type[]
                {
                    typeof(Core.ExtensionBase)
                });
}

The plugin is basically just an empty dll but it still throws the same error.

It only works if there are no shared types (But what's the point of plugin then?) like this:

private static PluginLoader GetDefaultLoader(string dllloc)
{
    return PluginLoader.CreateFromAssemblyFile(
                dllloc,
                isUnloadable: true,
                sharedTypes: new Type[] { });
}

The architecture of my project : image

Teknel commented 4 years ago

Do you have similar info in debugging output window?

Grendizr commented 3 years ago

Hi @natemcmaster ! Hi @Teknel ! Hi @ShankarBUS !

Any update on this one ? It seems I am getting into same pitfall.

Then as soon as I update the core library with the UseWPF flag set to true. It breaks on

{"Could not load file or assembly 'System.Data.SqlClient, Version=4.6.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'. The system cannot find the file specified.":"System.Data.SqlClient, Version=4.6.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"}

Thanks for your feedback.

Grendizr commented 3 years ago

Yet another piece of information.

I tried to upgrade everything to .Net 5 (.Net Core 5) and still fails but exception is a bit different.

Could not load file or assembly 'System.Data.SqlClient, Version=0.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'. The system cannot find the file specified.

Note the version number is not set.

Grendizr commented 3 years ago

I think, I have found a solution that could explain the behavior and that works for me .. it is named Microsoft.Windows.Compatibility

pmuessig commented 3 years ago

@Grendizr I am in a very similar position as you. However adding the Microsoft.Windows.Compatibility to my core library changed the exception to 2021-04-22 15:12:41.5923 ERROR System.IO.FileNotFoundException: Could not load file or assembly 'System.Data.OleDb, Version=4.0.1.1, Culture=neutral, PublicKeyToken=31bf3856ad364e35'. The system cannot find the file specified.

So strange!

Do you have any further luck on this?

pmuessig commented 3 years ago

To help those coming back here I had to do the following to get it to work:

Situation: I have a dotnetcore 3.1 lib that is of the type: <Project Sdk="Microsoft.NET.Sdk.WindowsDesktop" ... with <UseWPF>true</UseWPF> I use this project to contain common things that the plugins rely on. The plugins can have UI configuration, which they themselves provide, but having a common set of controls they can use is helpful.

In this core project i had to add the following additional properties

<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>    
    <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
    <GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
    <RestoreProjectStyle>PackageReference</RestoreProjectStyle>

I then had to add a package reference to <PackageReference Include="Microsoft.Windows.Compatibility" Version="3.1.1" />

This still resulted in some issues - namely that a specifc version of System.Data.Odbc could not be loaded. Noticed that there were no binding redirects generated in my config file for this dll, so adding an app.config to the project with the following got me further:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="System.Data.Odbc" publicKeyToken="31bf3856ad364e35" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-4.6.1.2" newVersion="4.6.1.2" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
</configuration>

This made progress, but still resulted in complaints about the right version of System.Data.OleDb not loaded. Noticed this binding redirect was also missing, but no matter what i tried to do to manually add it, no luck, so instead I added it as a package reference:

<PackageReference Include="System.Data.OleDb" Version="4.7.1" />

And voila, it worked.

@natemcmaster - IMHO, this dance sucks, I dont even use any System.Data.* libs. I wish there was an easier way to get this to work without what seems like black dll-magic hackery. But at least there's a workaround

devna13 commented 3 years ago

I was getting "Could not load ..." Error and adding CopyLocalLockFileAssemblies tag solved my issue...

<PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
   <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
  </PropertyGroup>
ALDamico commented 2 years ago

I was getting the same error, but in my case I'm targeting net5.0-windows (Desktop application) in my host application and net5.0-windows (Class Library) in my Contracts and Plugin projects.

Following the steps that @pmuessig suggested I was finally able to get the plugin to load. Thank you a lot! I think it would be worthwhile to add his guide to the documentation somewhere.

EDIT: I forgot to mention, however, that in my case I didn't have any issues with missing oledb assemblies.

Smartisa commented 2 years ago

To help those coming back here I had to do the following to get it to work:

Situation: I have a dotnetcore 3.1 lib that is of the type: <Project Sdk="Microsoft.NET.Sdk.WindowsDesktop" ... with <UseWPF>true</UseWPF> I use this project to contain common things that the plugins rely on. The plugins can have UI configuration, which they themselves provide, but having a common set of controls they can use is helpful.

In this core project i had to add the following additional properties

<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>    
    <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
    <GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
    <RestoreProjectStyle>PackageReference</RestoreProjectStyle>

I then had to add a package reference to <PackageReference Include="Microsoft.Windows.Compatibility" Version="3.1.1" />

This still resulted in some issues - namely that a specifc version of System.Data.Odbc could not be loaded. Noticed that there were no binding redirects generated in my config file for this dll, so adding an app.config to the project with the following got me further:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="System.Data.Odbc" publicKeyToken="31bf3856ad364e35" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-4.6.1.2" newVersion="4.6.1.2" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
</configuration>

This made progress, but still resulted in complaints about the right version of System.Data.OleDb not loaded. Noticed this binding redirect was also missing, but no matter what i tried to do to manually add it, no luck, so instead I added it as a package reference:

<PackageReference Include="System.Data.OleDb" Version="4.7.1" />

And voila, it worked.

@natemcmaster - IMHO, this dance sucks, I dont even use any System.Data.* libs. I wish there was an easier way to get this to work without what seems like black dll-magic hackery. But at least there's a workaround

There is no concept of binding redirection in core, so it is invalid to create app.config

rokenbuzz commented 2 years ago

I am building with .net 6, creating a simple demonstration of my own with WPF user control plugins. After getting the Could not load System.Data.SqlClient issue, and finding this thread, I found adding Microsoft.Windows.Compatibility 6.0.0 fixed it, for now. I did not need anything project settings or config files, or OleDb reference. I'm still not really understanding the issue, for now.

github-actions[bot] commented 1 year ago

This issue has been automatically marked as stale because it has no recent activity. It will be closed if no further activity occurs. Please comment if you believe this should remain open, otherwise it will be closed in 14 days. Thank you for your contributions to this project.

github-actions[bot] commented 1 year ago

Closing due to inactivity. If you are looking at this issue in the future and think it should be reopened, please make a commented here and mention natemcmaster so he sees the notification.