natemcmaster / DotNetCorePlugins

.NET Core library for dynamically loading code

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

[Question] Plugins with the same dependency but different versions of types of dependency assembly

ChronosXYZ opened this issue · comments

Situation:
I have two plugins: Plugin1 and Plugin2. Plugin1 manages connection to the MongoDB, works as "connection wrapper" for other plugins. Plugin2 is a consumer of database connection, so it requests the API of DB from Plugin1.

Algorithm of requesting exported API of Plugin1 is showed in following scheme:
изображение

Depends() and GetExportedAPI() have dynamic as return type.

So, the problem is that when I try to convert the received object from a specific implementation that I cannot use directly in the code (MongoDatabaseImpl) to the interface (IMongoDatabase), an exception System.InvalidCastException: Unable to cast object of type 'MongoDB.Driver.MongoDatabaseImpl' to type 'MongoDB.Driver.IMongoDatabase'. is thrown. As I understand, CLR tries to isolate dependencies of each plugin I've loaded to the separated contexts. And so, even if types are really equal, it thinks that these types are different.

What do I need to configure in DotNetCorePlugins library to share types of plugins dependencies in common context?

Here is example how I load the plugins:

public IPluginAPI LoadPlugin(string pluginName)
{
            PluginLoader loader;
            var pluginDll = Path.Combine(_currentPluginFolderPath, pluginName, pluginName + ".dll");
            if (File.Exists(pluginDll))
            {
                Logging.Log.Debug("Found plugin " + pluginName);
                Logging.Log.Debug("Try to initialize plugin " + pluginName);
                loader = PluginLoader.CreateFromAssemblyFile(
                    pluginDll,
                    sharedTypes: new[] {
                                            // here a long list of shared types of host
                                        },
                    config => config.PreferSharedTypes = true
                );
            }
            else
            {
                throw new Exception("specified plugin is not found");
            }

            var pluginTypes = loader.LoadDefaultAssembly().GetTypes();

            IPluginAPI plugin = null;
            foreach (var pluginType in pluginTypes
                    .Where(t => typeof(IPluginAPI).IsAssignableFrom(t) && !t.IsAbstract))
            {
                // This assumes the implementation of IPlugin has a parameterless constructor
                plugin = (IPluginAPI)Activator.CreateInstance(pluginType);
                Logging.Log.Debug($"Created plugin instance '{plugin.GetPluginUniqueName()}'.");
                var exportedTypes = plugin.GetExportedTypes();
                if (exportedTypes != null)
                {
                    foreach (var type in exportedTypes)
                    {
                        var path = new Uri(type.Module.Assembly.CodeBase).LocalPath;
                        AssemblyLoadContext.Default.LoadFromAssemblyPath(path);
                    }
                }
                plugin.PreInitialize(this);
                plugin.Initialize(_pluginHostAPI);
            }
            return plugin;
}

Btw, I've looked at #60 and #127, and tried to put exported types (in my case IMongoDatabase) of Plugin1 to default host context (see above), but nothing is changed - InvalidCastException remained as before. But I noticed that Plugin2 started using MongoDB.Driver.Core.dll assembly of Plugin1.

Your code snippets are helpful, but still not enough for me to determine what is going wrong. Can you share the full project which reproduces the issue?

Based on what you are sharing, this sound like the same issue as #60 - types that you want to share between plugins must be defined in host application and specified in sharedTypes. I see your snippet calls AssemblyLoadContext.Default.LoadFromAssemblyPath. This has unclear purpose to me, but if you're trying to use this as a way to share from Plugin1 into Plugin2, that's probably not going to work as this call just loads a second version of your assembly without unifying types.

Your code snippets are helpful, but still not enough for me to determine what is going wrong. Can you share the full project which reproduces the issue?

Based on what you are sharing, this sound like the same issue as #60 - types that you want to share between plugins must be defined in host application and specified in sharedTypes. I see your snippet calls AssemblyLoadContext.Default.LoadFromAssemblyPath. This has unclear purpose to me, but if you're trying to use this as a way to share from Plugin1 into Plugin2, that's probably not going to work as this call just loads a second version of your assembly without unifying types.

Yeah, my issue is the same as #60. I will share you the full code of my project today.

Thanks for the code. Yes, this confirms that the problem is a duplicate of #60.

There is currently no support for exporting types from your plugin to be shared with the host or other plugins. That is the intentional design of this library. To fix your problem, move your MongoDB dependency into the host application and add to sharedTypes.

I do not have plans to support cross-plugin communication or type unification. It's an impossible problem to solve in a generic, abstract way that works for all use cases. if you find a way to make cross-plugin communication work well, feel free to share the details of your project. If I find enough users taking a common approach, perhaps this library could add API to model a solution to this problem. So far, however, I haven't seen a good resolution to the problem.

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.

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.