C#插件架构,插件之间的接口份额 [英] C# Plugin Architecture with interfaces share between plugins

查看:233
本文介绍了C#插件架构,插件之间的接口份额的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我分了我的问题成短和长版本手头一点时间的人。

I divided my problem into a short and a long version for the people with little time at hand.

短版:

我需要与供应商和消费者的插件系统的一些架构。
供应商应实施intereface IProvider和消费者应实施IConsumer。
该执行的应用程序应该只知道IProvider和IConsumer的。
一个消费者实现可以要求执行的程序集(由ServiceProcessor的装置)供应商实施InterfaceX,并得到一个列表。
这些IProvider物体应被浇铸到InterfaceX(在消费者)能够钩住消费者到某些事件InterfaceX定义。这将失败,因为执行的程序集莫名其妙不知道这InterfaceX类型(施法失败)。解决办法是包括InterfaceX到一些汇编,无论是插件和正在执行的程序集的引用,但这应该意味着每一个新的提供者/消费者对的编译和非常不理想的。

I need some architecture for a system with provider and consumer plugins. Providers should implement intereface IProvider and consumers should implement IConsumer. The executing application should only be aware of IProvider and IConsumer. A consumer implementation can ask the executing assembly (by means of a ServiceProcessor) which providers implement InterfaceX and gets a List back. These IProvider objects should be casted to InterfaceX (in the consumer) to be able to hook the consumer onto some events InterfaceX defines. This will fail because the executing assembly somehow doesn't know this InterfaceX type (cast fails). Solution would be to include InterfaceX into some assembly that both the plugins and the executing assembly reference but this should mean a recompile for every new provider/consumer pair and is highly undesireable.

有什么建议?

长版:

我正在开发一些通用的服务将使用插件实现可重用性的更高的水平。该服务包括使用提供者和消费者某种Observer模式实现的。提供者和使用者应该是主要的应用插件。让我先解释一下该服务的工作方式,列出我的项目是在我的解决方案。

I'm developing some sort of generic service that will use plugins for achieving a higher level of re-usability. The service consists of some sort of Observer pattern implementation using Providers and Consumers. Both providers and Consumers should be plugins for the main application. Let me first explain how the service works by listing the projects I have in my solution.

A计划:一个Windows服务项目托管所有的插件和基本功能。一个TestGUI Windows窗体项目用于更容易调试。从项目B的ServiceProcessor类的实例是做插件相关的东西。子文件夹消费者和供应商这个项目包含其中的每个子文件夹分别assebly拥有消费者或提供插件子文件夹。

Project A: A Windows Service project for hosting all plugins and basic functionality. A TestGUI Windows Forms project is used for easier debugging. An instance of the ServiceProcessor class from Project B is doing the plugin related stuff. The subfolders "Consumers" and "Providers" of this project contains subfolders where every subfolder holds a consumer or provider plugin assebly respectively.

项目B:A类库拿着ServiceProcessor类(做所有的插件加载和插件之间调度等),IConsumer和IProvider

Project B: A Class library holding the ServiceProcessor class (that does all plugin loading and dispatching between plugins, etc), IConsumer and IProvider.

项目C:一个类库,与项目B,由TestConsumer(实施IConsumer)和TestProvider(实现IProvider)的。一个附加的接口(ITEST,本身从IProvider派生)由TestProvider实施

Project C: A Class library, linked to project B, consisting of TestConsumer (implementing IConsumer) and TestProvider (implementing IProvider). An additional interface (ITest, itself derived from IProvider) is implemented by the TestProvider.

这里的目标是,消费者插件可以问哪提供商(至少实施IProvider)的ServiceProcessor它)。返回IProvider物体应被浇铸在IConsumer执行它实现了其他接口(ITEST),使得消费者可以挂钩的事件处理程序的ITEST事件

The goal here is that a Consumer plugin can ask the ServiceProcessor which Providers (implementing at least IProvider) it has). The returned IProvider objects should be casted to the other interface it implements (ITest) in the IConsumer implementation so that the consumer can hook event handlers to the ITest events.

在项目A开始,包含消费者和供应商的插件子文件夹被加载。下面是我到目前为止遇到了一些问题,并试图解决。

When project A starts, the subfolders containing the consumer and provider plugins are loaded. Below are some problems I've encountered so far and tried to solve.

ITEST用于驻留在项目C,因为这仅适用于方法和事件TestProvider和TestConsumer都知道的接口。总的想法是,以保持项目的简单和不知道什么样的插件与对方做的。

The interface ITest used to reside in Project C, since this only applies to methods and events TestProvider and TestConsumer are aware of. The general idea is to keep project A simple and unaware of what the plugins do with each other.

使用ITEST项目C可能会在那个注塑IProvider到ITEST的TestConsumer的初始化方法code(这个对子级不是一个单一的类库本身在实现ITEST一个对象被称为一个IConsumer对象失败)将出现一个无效的铸造错误。这个错误可以通过将ITest接口到B项目由项目A引用也可以解决。这是非常有害的,虽然,因为我们需要重新编译一个项目时,一个新的接口是建立

With ITest in project C there and code in the Initialize method of the TestConsumer that casts the IProvider to ITest (this whould not fail in a single class library itself when an object implementing ITest is known as an IConsumer object) an invalid casting error would occur. This error can be solved by placing the ITest interface into project B that is referenced by project A as well. It is highly unwanted though since we need to recompile project A when a new interface is build.

我试图把ITEST通过仅依赖项目C引用一个类库,因为只有供应商和消费者需要了解这个接口,但没有成功:加载插件的CLR时指出引用的项目不能被发现。这可以通过挂接在当前的AppDomain的AssemblyResolve事件得到解决,但不知何故,这似乎不需要为好。 ITEST再次回到B项目。

I tried to put ITest in a single class library referenced by project C only, since only the provider and consumer need to be aware of this interface, but with no success: when loading the plugin the CLR states the referenced project could not be found. This could be solved by hooking on the AssemblyResolve event of the current AppDomain but somehow this seems unwanted as well. ITest went back to Project B again.

我试着项目C分流成一个单独的项目对消费者和提供者无一不加载其本身做工精良的装配:这两个组件是居民大会集合或当前的AppDomain中:
大会发现:Datamex.Projects.Polaris.Testing.Providers,版本= 1.0.0.0,文化=中性公钥= 2813de212e2efcd3
大会发现:Datamex.Projects.Polaris.Testing.Consumers,版本= 1.0.0.0,文化=中性公钥= ea5901de8cdcb258

I tried to split project C into a separate project for the consumer and provider and both load the assemblies which itself work well: both assemblies are resident in the Assemblies collection or the current AppDomain: Assembly found: Datamex.Projects.Polaris.Testing.Providers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=2813de212e2efcd3 Assembly found: Datamex.Projects.Polaris.Testing.Consumers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=ea5901de8cdcb258

由于消费者使用引用从使用者到提供者作出的提供者。现在AssemblyResolve事件再次烧制说明它需要以下文件:
的AssemblyName = Datamex.Projects.Polaris.Testing.Providers,版本= 1.0.0.0,文化=中性公钥= 2813de212e2efcd3

Since the Consumer uses the Provider a reference was made from the Consumer to the Provider. Now the AssemblyResolve event fired again stating it needs the following file: AssemblyName=Datamex.Projects.Polaris.Testing.Providers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=2813de212e2efcd3

我的问题:
为什么是这样?该文件已加载正确的?
为什么从IProvider投一些接口,我知道这是不可能实现?这可能是因为在执行中的程序本身不知道这个接口,但无法在此动态加载

My questions: Why is this? This file is already loaded right? Why is the cast from IProvider to some interface I know it implements impossible? This is probably because the executing program itself doesn't know this interface, but can't this be loaded dynamically?

我的终极目标是:
消费者插件问它具有提供那些实现接口×的ServiceProcessor。该供应商可强制转换为这个接口×,不执行装配意识到接口x的。

My ultimate goal: Consumer plugins ask the ServiceProcessor which Providers it has that do implement Interface x. The providers can be casted to this interface x, without executing assembly being aware of interface x.

有人可以帮助?

在此先感谢,
埃里克

Thanks in advance, Erik

推荐答案

我只是想作为最好的,我可以重新创建你的解决方案,我没有这样的问题。 (警告,大量code样本后续....)

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
{        
}

无论是应用程序和插件项目中引用共享项目和插件的dll被复制到应用程序的搜索目录 - 但他们不相互引用

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.

在插件加载器构造找到所有的IProviders然后创建所有的IConsumers并呼吁初始化它们。内部初始化使用者c进行TestProvider被构造和返回可从加载并在这$ C $的情况下请求提供者。所有这一切对我的作品与组件的装载没有华丽的控制。

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.

这篇关于C#插件架构,插件之间的接口份额的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆