C# Plugin Architecture with interfaces share between plugins

試著忘記壹切 提交于 2019-11-27 18:24:42

I just tried to recreate your solution as best as I can, and I have no such issues. (Warning, lots of code samples follow....)

First project is the application, this contains one class:

public class PluginLoader : ILoader
{
    private List<Type> _providers = new List<Type>();

    public PluginLoader()
    {
        LoadProviders();
        LoadConsumers();
    }

    public IProvider RequestProvider(Type providerType)
    {
        foreach(Type t in _providers)
        {
            if (t.GetInterfaces().Contains(providerType))
            {
                return (IProvider)Activator.CreateInstance(t);
            }
        }
        return null;
    }

    private void LoadProviders()
    {
        DirectoryInfo di = new DirectoryInfo(PluginSearchPath);
        FileInfo[] assemblies = di.GetFiles("*.dll");
        foreach (FileInfo assembly in assemblies)
        {
            Assembly a = Assembly.LoadFrom(assembly.FullName);
            foreach (Type type in a.GetTypes())
            {
                if (type.GetInterfaces().Contains(typeof(IProvider)))
                {
                    _providers.Add(type);
                }
            }
        }

    }

    private void LoadConsumers()
    {
        DirectoryInfo di = new DirectoryInfo(PluginSearchPath);
        FileInfo[] assemblies = di.GetFiles("*.dll");
        foreach (FileInfo assembly in assemblies)
        {
            Assembly a = Assembly.LoadFrom(assembly.FullName);
            foreach (Type type in a.GetTypes())
            {
                if (type.GetInterfaces().Contains(typeof(IConsumer)))
                {
                    IConsumer consumer = (IConsumer)Activator.CreateInstance(type);
                    consumer.Initialize(this);
                }
            }
        }
    }

Obviously this can be tidied up enormously.

Next project is the shared library which contains the following three interfaces:

public interface ILoader
{
    IProvider RequestProvider(Type providerType);
}

public interface IConsumer
{
    void Initialize(ILoader loader);
}

public interface IProvider
{
}

Finally there is the plugin project with these classes:

public interface ITest : IProvider
{        
}

public class TestConsumer : IConsumer
{
    public void Initialize(ILoader loader)
    {
        ITest tester = (ITest)loader.RequestProvider(typeof (ITest));
    }
}

public class TestProvider : ITest
{        
}

Both the application and the plugin projects reference the shared project and the plugin dll is copied to the search directory for the application - but they don't reference one another.

When the PluginLoader is constructed it finds all the IProviders then creates all the IConsumers and calls Initialize on them. Inside the initialize the consumer can request providers from the loader and in the case of this code a TestProvider is constructed and returned. All of this works for me with no fancy control of the loading of assemblies.

It is still in development, but is sounds like a perfect usecase for MEF (to be included in .Net 4) and used internally in VS2010.

MEF presents a simple solution for the runtime extensibility problem. Until now, any application that wanted to support a plugin model needed to create its own infrastructure from scratch. Those plugins would often be application-specific and could not be reused across multiple implementations.

Previews are already available on http://www.codeplex.com/MEF

The blog of Glen Block can also be useful.

You might find my articles useful to see a working example of a plugin framework, and how these issues are addressed by creating a common assembly holding the interfaces:

Plugins in C# Basic Tutorial:

http://www.codeproject.com/KB/cs/pluginsincsharp.aspx

Followup article, with a Generics enabled Plugin Manager Library:

http://www.codeproject.com/KB/cs/ExtensionManagerLibrary.aspx

If you're question is, how can two unrelated assemblies share the same interface, the answer is 'you cannot' A solution is to include the interface in all assemblies, perhaps in a dll that the plugin-builders can reference, and in your loading assembly.

I've done something similar to what you are trying to do and as long as I had the assemblies in a place where the loader looked automatically, I didn't encounter any problems.

Have you tried putting all your assemblies in a subdirectory underneath where the exe resides? I can't remember the details now but there's a list of steps documented as to exactly where and in what order the loader looks for assemblies/types.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!