Eri*_*jer 16 c# plugins interface observer-pattern
我把我的问题分成了一个简短的版本,适用于手头很少的人.
精简版:
我需要一些具有提供者和消费者插件的系统架构.提供商应实施interface IProvider,消费者应实施IConsumer.执行的应用程序应该只知道IProvider和IConsumer.消费者实现可以向执行程序集(通过ServiceProcessor)询问哪些提供程序实现InterfaceX并获取List.这些IProvider对象应该被转换为InterfaceX(在使用者中),以便能够将使用者挂钩到InterfaceX定义的某些事件上.这将失败,因为执行程序集以某种方式不知道此InterfaceX类型(强制转换失败).解决方案是将InterfaceX包含在插件和执行程序集引用的某个程序集中,但这应该意味着对每个新的提供者/消费者对进行重新编译并且非常不受欢迎.
有什么建议?
长版:
我正在开发某种通用服务,它将使用插件来实现更高级别的可重用性.该服务包含使用提供者和使用者的某种Observer模式实现.提供者和使用者都应该是主应用程序的插件.让我首先通过列出我在解决方案中的项目来解释服务的工作原理.
项目A:用于托管所有插件和基本功能的Windows服务项目.TestGUI Windows窗体项目用于更轻松的调试.来自Project B的ServiceProcessor类的一个实例正在执行与插件相关的东西.此项目的子文件夹"Consumers"和"Providers"包含子文件夹,其中每个子文件夹分别包含一个使用者或提供者插件.
项目B:包含ServiceProcessor类的类库(在插件之间执行所有插件加载和调度等),IConsumer和IProvider.
项目C:链接到项目B的类库,由TestConsumer(实现IConsumer)和TestProvider(实现IProvider)组成.TestProvider实现了一个额外的接口(ITest,本身派生自IProvider).
这里的目标是消费者插件可以向ServiceProcessor询问它所拥有的提供者(至少实现IProvider).返回的IProvider对象应该被转换到它在IConsumer实现中实现的其他接口(ITest),以便使用者可以将事件处理程序挂钩到ITest事件.
当项目A启动时,将加载包含使用者和提供者插件的子文件夹.以下是我到目前为止遇到的一些问题并试图解决.
ITest用于驻留在Project C中的接口,因为这仅适用于TestProvider和TestConsumer所知道的方法和事件.一般的想法是保持项目A简单并且不知道插件彼此做什么.
使用项目C中的ITest和TestConsumer的Initialize方法中的代码将IProvider转换为ITest(当实现ITest的对象被称为IConsumer对象时,这不会在单个类库中失败)会发生无效的转换错误.通过将ITest接口放入项目A引用的项目B中,可以解决此错误.这是非常不受欢迎的,因为我们需要在构建新接口时重新编译项目A.
我试图将ITest放在由项目C引用的单个类库中,因为只有提供者和使用者需要知道这个接口,但没有成功:当加载插件时,CLR声明无法找到引用的项目.这可以通过挂钩当前AppDomain的AssemblyResolve事件来解决,但不知何故这似乎也是不需要的.ITest再次回到了B项目.
我尝试将项目C拆分为消费者和提供者的单独项目,并且都加载本身运行良好的程序集:两个程序集都驻留在Assemblies集合中或当前的AppDomain:程序集找到:Datamex.Projects.Polaris.Testing.Providers ,版本= 1.0.0.0,文化=中性公钥= 2813de212e2efcd3大会发现:Datamex.Projects.Polaris.Testing.Consumers,版本= 1.0.0.0,文化=中立,公钥= ea5901de8cdcb258
由于消费者使用提供者,因此从消费者到提供者进行了引用.现在,AssemblyResolve事件再次触发,表明它需要以下文件:AssemblyName = Datamex.Projects.Polaris.Testing.Providers,Version = 1.0.0.0,Culture = neutral,PublicKeyToken = 2813de212e2efcd3
我的问题:这是为什么?这个文件已经加载了吗?为什么从IProvider到某些界面的演员我知道它实现了不可能?这可能是因为执行程序本身不知道这个接口,但不能动态加载?
我的最终目标:消费者插件向ServiceProcessor询问它实现接口x的提供商.提供程序可以转换为此接口x,而不执行程序集知道接口x.
有人可以帮忙吗?
提前谢谢,Erik
Mar*_*ris 15
我只是尝试尽可能地重新创建您的解决方案,而且我没有这样的问题.(警告,许多代码示例如下......)
第一个项目是应用程序,它包含一个类:
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);
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
显然,这可以大大整理.
下一个项目是共享库,它包含以下三个接口:
public interface ILoader
{
IProvider RequestProvider(Type providerType);
}
public interface IConsumer
{
void Initialize(ILoader loader);
}
public interface IProvider
{
}
Run Code Online (Sandbox Code Playgroud)
最后是包含这些类的插件项目:
public interface ITest : IProvider
{
}
public class TestConsumer : IConsumer
{
public void Initialize(ILoader loader)
{
ITest tester = (ITest)loader.RequestProvider(typeof (ITest));
}
}
public class TestProvider : ITest
{
}
Run Code Online (Sandbox Code Playgroud)
应用程序和插件项目都引用共享项目,插件dll被复制到应用程序的搜索目录中 - 但它们不会相互引用.
当构造PluginLoader时,它会找到所有的IProviders然后创建所有IConsumers并调用它们的Initialize.在初始化内部,消费者可以从加载器请求提供者,并且在此代码的情况下,构造并返回TestProvider.所有这些对我来说都是有效的,没有花哨的控制装配的装载.