IsAssignableFrom returns _false_ on published dllI currently see this plugin example.
IsAssignableFrom returns true when I run the project using dotnet run in the workspace but it returns _false_ when I publishing the solution and run it.
The publish and run command is below
dotnet publish -c Release --output .\out .\AppWithPlugin.sln
cd .\out\
dotnet .\AppWithPlugin.dll
I replaced all plugin paths as relative to AppWithPlugin.dll and I checked it finds XXXPlugin.dll correctly, but isAssignalbleFrom returns false and throws this.
System.ApplicationException: Can't find any type which implements ICommand in HelloPlugin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null from C:\Users\somnus\Documents\workspace\samples\core\extensions\AppWithPlugin\out\HelloPlugin.dll.
Available types: HelloPlugin.HelloCommand
$ dotnet --version
3.0.100-preview3-010431
$ [System.Environment]::OSVersion.Version
Major Minor Build Revision
----- ----- ----- --------
10 0 17134 0
@jeffschwMSFT @vitek-karas @sdmaclea can you please take a look and help me route it?
This belongs to the dotnet/samples repo, but it's probably easier to resolve it here... (and I can't transfer it anyway)
@lesomnus The problem is that the sample was written to not expect the plugins to live in the same directory as the app. Publishing the app the way it is will copy all of the files into the same location.
The real problem is the PluginBase.dll. When you just build the app (not publish) each plugin lives in its own directory and is setup to not copy the PluginBase.dll next to itself. As such when the app is loading the plugins it won't find the PluginBase.dll next to them and the load context will fall back to the AssemblyLoadContext.Default which will return the one copy of PluginBase which was loaded by the main app. This is crucial for the app to work - the PluginBase must be only loaded by the main app, so that all of the types in it (namely ICommand) only exists once and thus can be compared and accessed in all the plugins.
Publishing the app along with all the plugins into the same folder means that when the plugins are loaded they will find the PluginBase.dll next to them and will load it into their own AssemblyLoadContext instaces - as such each plugin will get its own copy of PluginBase assembly and namely its own copy of ICommand. This means that the ICommand the main app has is not the same ICommand as the plugins have and as such the assignment check will fail.
There are several way to fix this
PluginsBase would not be there and thus the problem would go away.PluginBase into the app's loading logic. This can be done by modifying the PluginLoadContext.Load method - simply add this at the beginning of the method:C#
if (assemblyName.FullName == typeof(ICommand).Assembly.FullName)
{
return null;
}
This will make sure that the assembly which defines ICommand (which is PluginBase) will never be loaded into the plugin's own ALC, instead returning null means search for it in the default ALC - which will return the one instance loaded by the main app.
The sample is relatively simplistic with regard to handling the plugin build/deployment. As is it relies on certain layout on disk. Adding the above piece of code makes it more resilient, but it then requires knowledge of the assembly layout in the loader. Each has its own pros and cons, depending on your situation.
Thanks for your help with the rich explanation.
The plugins are loaded well!
Most helpful comment
This belongs to the dotnet/samples repo, but it's probably easier to resolve it here... (and I can't transfer it anyway)
@lesomnus The problem is that the sample was written to not expect the plugins to live in the same directory as the app. Publishing the app the way it is will copy all of the files into the same location.
The real problem is the
PluginBase.dll. When you just build the app (not publish) each plugin lives in its own directory and is setup to not copy thePluginBase.dllnext to itself. As such when the app is loading the plugins it won't find thePluginBase.dllnext to them and the load context will fall back to theAssemblyLoadContext.Defaultwhich will return the one copy ofPluginBasewhich was loaded by the main app. This is crucial for the app to work - thePluginBasemust be only loaded by the main app, so that all of the types in it (namelyICommand) only exists once and thus can be compared and accessed in all the plugins.Publishing the app along with all the plugins into the same folder means that when the plugins are loaded they will find the
PluginBase.dllnext to them and will load it into their ownAssemblyLoadContextinstaces - as such each plugin will get its own copy ofPluginBaseassembly and namely its own copy ofICommand. This means that theICommandthe main app has is not the sameICommandas the plugins have and as such the assignment check will fail.There are several way to fix this
PluginsBasewould not be there and thus the problem would go away.PluginBaseinto the app's loading logic. This can be done by modifying thePluginLoadContext.Loadmethod - simply add this at the beginning of the method:C# if (assemblyName.FullName == typeof(ICommand).Assembly.FullName) { return null; }This will make sure that the assembly which defines
ICommand(which isPluginBase) will never be loaded into the plugin's own ALC, instead returning null means search for it in the default ALC - which will return the one instance loaded by the main app.The sample is relatively simplistic with regard to handling the plugin build/deployment. As is it relies on certain layout on disk. Adding the above piece of code makes it more resilient, but it then requires knowledge of the assembly layout in the loader. Each has its own pros and cons, depending on your situation.