gpt4 book ai didi

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

转载 作者:太空狗 更新时间:2023-10-30 01:21:36 24 4
gpt4 key购买 nike

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

[编辑]

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

我尝试的是将插件放在子目录中,然后递归地遍历这些目录以将插件添加到目录中。但是,这不起作用。在子目录中找到的第二个插件将被导入,但这个插件是针对第一个插件的。当循环遍历容器 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,每个针对不同的 Controller (读作:与不同的网络服务器通信)。 6 个 ControllerAdapter 中的逻辑是相等的。

我认为复制 ClassLibrary(例如复制到 1.dll、2.dll 等)并添加配置文件(1.dll.config 等)应该可以解决问题,但不是。

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

我需要在导出时对元数据做些什么吗?

导入服务器

[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 解决方案相比,这可能更多地与您如何使用程序集有关。

你说:

The logic in the 6 ControllerAdapters is equal.

那么是同一个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: 1AdapterPlugin.ControllerAdapterAdapterPlugin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=nullfile:///C:/USERS/GARKIN/DOCUMENTS/VISUAL STUDIO 2012/PROJECTS/MEFADAPTERS/ADAPTERSERVER/BIN/DEBUG/PLUGINS/ADAPTER1/ADAPTERPLUGIN.DLL

Test result for two plugins in place, using the same plugin assembly copied over to two distinct plugin directories (probably your case):

Plugins found: 2AdapterPlugin.ControllerAdapterAdapterPlugin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=nullfile:///C:/USERS/GARKIN/DOCUMENTS/VISUAL STUDIO 2012/PROJECTS/MEFADAPTERS/ADAPTERSERVER/BIN/DEBUG/PLUGINS/ADAPTER1/ADAPTERPLUGIN.DLLAdapterPlugin.ControllerAdapterAdapterPlugin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=nullfile:///C:/USERS/GARKIN/DOCUMENTS/VISUAL STUDIO 2012/PROJECTS/MEFADAPTERS/ADAPTERSERVER/BIN/DEBUG/PLUGINS/ADAPTER1/ADAPTERPLUGIN.DLL

You also get the exact same result if you give distinct names to those DLLs because effectively it is still the same assembly inside.

Now I add the third plugin, but this time it is a different plugin assembly:

Plugins found: 3AdapterPlugin.ControllerAdapterAdapterPlugin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=nullfile:///C:/USERS/GARKIN/DOCUMENTS/VISUAL STUDIO 2012/PROJECTS/MEFADAPTERS/ADAPTERSERVER/BIN/DEBUG/PLUGINS/ADAPTER1/ADAPTERPLUGIN.DLLAdapterPlugin.ControllerAdapterAdapterPlugin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=nullfile:///C:/USERS/GARKIN/DOCUMENTS/VISUAL STUDIO 2012/PROJECTS/MEFADAPTERS/ADAPTERSERVER/BIN/DEBUG/PLUGINS/ADAPTER1/ADAPTERPLUGIN.DLLAdapterPlugin2.ControllerAdapterAdapterPlugin2, Version=1.0.0.0, Culture=neutral, PublicKeyToken=nullfile:///C:/USERS/GARKIN/DOCUMENTS/VISUAL STUDIO 2012/PROJECTS/MEFADAPTERS/ADAPTERSERVER/BIN/DEBUG/PLUGINS/ADAPTER3/ADAPTERPLUGIN2.DLL

The different assembly is of course found and identified correctly.

So this all boils down to how the .NET runtime handles assembly loading, which is a complex and strictly defined process and works differently for strongly and weakly named assemblies. I recommend this article for a good explanation of the process: Assembly Load Contexts Subtleties.

In this case, the same process is followed behind the scenes when using MEF:

  1. The .NET runtime finds the first weakly typed plugin assembly and loads it from that location and MEF does it's export processing.

  2. Then MEF tries to process the next plugin assembly it founds using the catalog, but the runtime sees the assembly with the same metadata already loaded. So it uses the already loaded one to look for exports and ends up instantiating the same type again. It does not touch the second DLL at all.

There is no way the same assembly can be loaded more than once by the runtime. Which makes perfect sense when you think of it. Assembly is just bunch of types with their metadata and once loaded the types are available, no need to load them again.

This may not be completely correct, but I hope it helps to explain where the problem lies and it should be clear that duplicating DLLs for this purpose is useless.

Now regarding what you want to achieve. It seems that all you need is just to get multiple instances of the SAME adapter plugin to use them for different purposes, which has nothing to do with multiplying DLLs.

To get multiple adapter instances you can define multiple imports with RequiredCreationPolicy set to CreationPolicy.NonShared in your server which MEF will accordingly instantiate for you:

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 实例化。

关于c# - 如何根据 app.config 多次导出一个 MEF 插件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15131014/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com