Reputation: 815
I have a main console application and a plugin as a dll. Plugins must implement IPlugin interface. These are three projects
1) Main console application project
2) Plugin project
3) Abstraction project which has IPlugin interface which is used by both (1) & (2) projects
The plugins are copied to a folder under main console application. So project structure is like below. Here the ...Messaging folder has the main plugin dll and it's references.
Note that IPlugin.dll is in two places (under main console app and plugin folder)
Then I load the plugin dynamically using AssemblyLoadContext below
class JobLoadContext : AssemblyLoadContext
{
private AssemblyDependencyResolver _resolver;
public JobLoadContext(string pluginPath)
{
_resolver = new AssemblyDependencyResolver(pluginPath);
}
protected override Assembly? Load(AssemblyName assemblyName)
{
string assemblyPath = _resolver.ResolveAssemblyToPath(assemblyName);
if (assemblyPath != null)
{
return LoadFromAssemblyPath(assemblyPath);
}
return null;
}
protected override IntPtr LoadUnmanagedDll(string unmanagedDllName)
{
string libraryPath = _resolver.ResolveUnmanagedDllToPath(unmanagedDllName);
if (libraryPath != null)
{
return LoadUnmanagedDllFromPath(libraryPath);
}
return IntPtr.Zero;
}
}
In the main console application I use above class to Load the plugins and it's dependencies which is in the subdirectory. Once the plugin is loaded; I query for types implementing Iplugin interface and calls a Init() method in the plugin. That is wired up as below.
var loadContext = new JobLoadContext(path);
var asm = loadContext.LoadFromAssemblyName(new AssemblyName(Path.GetFileNameWithoutExtension(path)));
Once the assembly is loaded I query for types that implements Iplugin interface like;
var moduleInitializerType = asm.GetTypes().FirstOrDefault(t => typeof(IPlugin).IsAssignableFrom(t));
//other code to call Init() method inside plugin that implements IPlugin interface
The issue is moduleInitializerType variable is always null! However if I type the asm.GetTypes()….. Statement in watch window it shows the type.
I feel this is related to how the loadcontext works. Note that there is a IPlugin.Dll loaded by main application and another IPlugin.dll loaded to my custom loadcontext (even though they are same version etc…). So from main application, when I ask for types that implement IPlugin.dll like;
var moduleInitializerType = asm.GetTypes().FirstOrDefault(t => typeof(IPlugin).IsAssignableFrom(t));
The typeof(Iplugin) may be refering to the dll in main application folder. But the MyPlugin.dll loaded by my custom loadcontext has a reference to IPlugin interface in it's own directory. This may be the conflict why it can't find the class implementing IPlugin interface. However it's very strange that the same statement works in the watch window. If I let the application to just run it crashes anyway.
I tried using AssemblyLoadContext.Default.LoadFromAssemblyName(….) to load my plugins but in that case it fails to load some dependency dlls in the plugin folder. All of these problems go away If I add my plugin project to main application and compile.
Is there a way to may be merge loadcontexts properly so I copy my custom loadcontext data to AssemblyContext.Default which contains static references to main project?
Has anyone come across this problem?
Upvotes: 0
Views: 1850
Reputation: 27307
Your guess is right, check this article: https://learn.microsoft.com/en-us/dotnet/core/dependency-loading/loading-managed#algorithm
Because you explictly loaded IPlugin.Dll from the plugin folder, in fact it was already loaded into AssemblyLoadContext.Default by your application, any other AssemblyLoadContext instances will look for assemblies from AssemblyLoadContext.Default after Load
fails, so you need to skip the step (or simply delete IPlugin.Dll in the plugin folder).
protected override Assembly? Load(AssemblyName assemblyName)
{
if (assemblyName.Name == "IPlugin")
return null;
....
}
Upvotes: 0