繁体   English   中英

如何根据app.config多次导出一个MEF插件?

[英]How to export one MEF plugin multiple times, depending on app.config?

我正在构建一个简单的MEF应用程序。 我想要实现的是构建一个插件,可以在同一个组合应用程序中多次注册。 插件的注册应该取决于插件的配置文件中的设置,但我无法做到这一点。

[编辑]

我的服务器具有CompositionContainer,需要与6个不同的目标(即交通灯控制器)进行通信。 对于每个目标,我想添加一个插件。 插件逻辑是一样的,所以我想只维护一个插件。 每个目标都有自己的webaddress进行通信(以及其他一些配置项),我希望它们在(单独的)配置文件中。

我尝试的是将插件放在子目录中并递归遍历这些目录以在目录中添加插件。 但这不起作用。 将导入在子目录中找到的第二个插件,但是这个插件是针对第一个插件的。 当循环通过容器FASTAdapters时,所有部分似乎都等于第一个。

private void Compose()
{
    var catalog = new AggregateCatalog();
    string sDir = AppSettingsUtil.GetString("FASTAdaptersLocation", @"./Plugins");
    foreach (string d in Directory.GetDirectories(sDir))
    {
        catalog.Catalogs.Add(new DirectoryCatalog(d));
    }
    var container = new CompositionContainer(catalog);
    container.ComposeParts(this);
}

我不知道我是否也可以使用ExportMetadata属性。 似乎必须对ExportMetadata属性进行硬编码,但是如果可能的话,我希望从配置文件中读取属性。

[/编辑]

我的目标是拥有6个ControllerAdapter,每个都适用于不同的控制器(读取:与不同的Web服务器通信)。 6 ControllerAdapters中的逻辑是相同的。

我想复制ClassLibrary(例如1.dll,2.dll等)并添加configfiles(1.dll.config等)应该可以做到,但不是。

在编写时,我在容器中获得了多个实例typeof(FAST.DevIS.ControllerAdapter) ,但我不知道如何进一步。

我是否需要在导出中使用MetaData?

导入服务器

[ImportMany]
public IEnumerable<IFASTAdapter> FASTAdapters { get; set; }

private void Compose()
{
    var catalog = new AggregateCatalog();
    catalog.Catalogs.Add(new DirectoryCatalog(AppSettingsUtil.GetString("FASTAdaptersLocation", Path.GetDirectoryName(Assembly.GetAssembly(typeof(ControllerServer)).Location))));
    var container = new CompositionContainer(catalog);
    container.ComposeParts(this);
}

插件

namespace FAST.DevIS.ControllerAdapter
{
   [Export (typeof(IFASTAdapter))]
   public class ControllerAdapter : IFASTAdapter
   {
       ...
   }
}

界面

namespace FAST.Common.FastAdapter
{
    public interface IFASTAdapter
    {
        /// Parse plan parameters
        /// 
        //Activator
        bool ParsePlan(PlansContainer plan);
        bool ActivatePlan();
        void Configure(string config);
    }
}

与使用MEF解决方案相比,使用组件的方式可能更为严重。

你说:

6 ControllerAdapters中的逻辑是相同的。

那么同样的DLL只是复制了6次到不同的插件目录? 如果是,那么这就是问题所在。

我模拟了你的方法并运行了一些测试来证明我的想法。 代码实际上与您的代码相同,并从服务器的bin / plugin目录的子目录中读取插件。

使用NUnit进行简单测试来练习服务器类库:

[Test]
public void Compose()
{
    var server = new Server();
    server.Compose();
    Console.WriteLine("Plugins found: " + server.FASTAdapters.Count());
    Console.WriteLine();
    foreach (var adapter in server.FASTAdapters)
    {
        Console.WriteLine(adapter.GetType());
        Console.WriteLine(adapter.GetType().Assembly.FullName);
        Console.WriteLine(adapter.GetType().Assembly.CodeBase);
        Console.WriteLine();
    }
    Assert.Pass();
}

一个插件的测试结果到位:

Plugins found: 1

AdapterPlugin.ControllerAdapter
AdapterPlugin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
file:///C:/USERS/GARKIN/DOCUMENTS/VISUAL STUDIO 2012/PROJECTS/MEFADAPTERS/ADAPTERSERVER/BIN/DEBUG/PLUGINS/ADAPTER1/ADAPTERPLUGIN.DLL

两个插件的测试结果到位,使用相同的插件程序集复制到两个不同的插件目录(可能是你的情况):

Plugins found: 2

AdapterPlugin.ControllerAdapter
AdapterPlugin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
file:///C:/USERS/GARKIN/DOCUMENTS/VISUAL STUDIO 2012/PROJECTS/MEFADAPTERS/ADAPTERSERVER/BIN/DEBUG/PLUGINS/ADAPTER1/ADAPTERPLUGIN.DLL

AdapterPlugin.ControllerAdapter
AdapterPlugin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
file:///C:/USERS/GARKIN/DOCUMENTS/VISUAL STUDIO 2012/PROJECTS/MEFADAPTERS/ADAPTERSERVER/BIN/DEBUG/PLUGINS/ADAPTER1/ADAPTERPLUGIN.DLL

如果为这些DLL提供不同的名称,也会得到完全相同的结果,因为它实际上仍然是相同的程序集。

现在我添加第三个插件,但这次它是一个不同的插件程序集:

Plugins found: 3

AdapterPlugin.ControllerAdapter
AdapterPlugin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
file:///C:/USERS/GARKIN/DOCUMENTS/VISUAL STUDIO 2012/PROJECTS/MEFADAPTERS/ADAPTERSERVER/BIN/DEBUG/PLUGINS/ADAPTER1/ADAPTERPLUGIN.DLL

AdapterPlugin.ControllerAdapter
AdapterPlugin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
file:///C:/USERS/GARKIN/DOCUMENTS/VISUAL STUDIO 2012/PROJECTS/MEFADAPTERS/ADAPTERSERVER/BIN/DEBUG/PLUGINS/ADAPTER1/ADAPTERPLUGIN.DLL

AdapterPlugin2.ControllerAdapter
AdapterPlugin2, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
file:///C:/USERS/GARKIN/DOCUMENTS/VISUAL STUDIO 2012/PROJECTS/MEFADAPTERS/ADAPTERSERVER/BIN/DEBUG/PLUGINS/ADAPTER3/ADAPTERPLUGIN2.DLL

当然可以正确地找到并识别不同的组件。

所以这一切都归结为.NET运行时如何处理程序集加载,这是一个复杂且严格定义的过程,对于强名称和弱名称程序集的工作方式不同。 我推荐这篇文章是为了对过程的一个很好的解释: 程序集加载上下文细微之处

在这种情况下,使用MEF后,在幕后执行相同的过程:

  1. .NET运行时找到第一个弱类型的插件程序集并从该位置加载它,MEF执行它的导出处理。

  2. 然后MEF尝试使用目录处理它创建的下一个插件程序集,但运行时会看到已加载相同元数据的程序集。 因此它使用已经加载的一个来查找导出并最终再次实例化相同的类型。 它根本不触及第二个DLL。

运行时无法多次加载同一个程序集。 当你想到它时,这是完美的意义。 汇编只是一堆带有元数据的类型,一旦加载,类型就可用,无需再次加载它们。

这可能不完全正确,但我希望它有助于解释问题所在,并且应该清楚为此目的复制DLL是没用的。

现在关于你想要实现的目标。 似乎所有你需要的只是获取SAME适配器插件的多个实例以将它们用于不同目的,这与乘法DLL无关。

要获取多个适配器实例,您可以在服务器中将RequiredCreationPolicy设置为CreationPolicy.NonShared来定义多个导入,MEF将相应地为您实例化:

public class Server
{
    [Import(RequiredCreationPolicy = CreationPolicy.NonShared)]
    public IFASTAdapter FirstAdapter { get; set; }

    [Import(RequiredCreationPolicy = CreationPolicy.NonShared)]
    public IFASTAdapter SecondAdapter { get; set; }

    // Other adapters ...

    public void Compose()
    {
        var catalog = new AggregateCatalog();
        var pluginsDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "plugins");
        foreach (string d in Directory.GetDirectories(pluginsDir))
        {
            catalog.Catalogs.Add(new DirectoryCatalog(d));
        }
        var container = new CompositionContainer(catalog);
        container.ComposeParts(this);
    }
}

相应的NUnit测试,以检查适配器是否已实例化,以及它们是不同的实例:

[Test]
public void Compose_MultipleAdapters_NonShared()
{
    var server = new Server();
    server.Compose();
    Assert.That(server.FirstAdapter, Is.Not.Null);
    Assert.That(server.SecondAdapter, Is.Not.Null);
    Assert.That(server.FirstAdapter, Is.Not.SameAs(server.SecondAdapter));
}

如果所有这些在某种程度上对您有所帮助,我们还可以查看您希望如何配置使用app.config实例化的内容和方式。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM