4

簡単なMEFアプリケーションを作成しています。私が達成したいのは、同じ構成アプリケーションに複数回登録できる1つのプラグインを作成することです。プラグインの登録は、プラグインのconfigfileの設定に依存する必要がありますが、これを行うことはできません。

[編集]

CompositionContainerを備えた私のサーバーは、6つの異なるターゲット(つまり、信号機コントローラー)と通信する必要があります。すべてのターゲットに対して、プラグインを追加したいと思います。プラグインのロジックは同じなので、1つのプラグインだけを維持したいと思います。すべてのターゲットには、通信するための独自のWebアドレス(およびその他の構成項目)があります。これらを(個別の)構成ファイルに含める必要があります。

私が試したのは、プラグインをサブディレクトリに配置し、それらのディレクトリを再帰的に調べて、カタログにプラグインを追加することです。ただし、これは機能しません。サブディレクトリにある2番目のプラグインがインポートされますが、これは最初のプラグインを対象としています。コンテナ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);
    }
}
4

1 に答える 1

5

これは、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();
}

1つのプラグインのテスト結果:

見つかったプラグイン: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

2つの異なるプラグインディレクトリにコピーされた同じプラグインアセンブリを使用して、2つのプラグインのテスト結果(おそらくあなたの場合):

見つかったプラグイン: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に個別の名前を付けた場合も、実際には内部で同じアセンブリであるため、まったく同じ結果が得られます。

次に、3番目のプラグインを追加しますが、今回は別のプラグインアセンブリです。

見つかったプラグイン: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ランタイムがアセンブリの読み込みを処理する方法に要約されます。これは複雑で厳密に定義されたプロセスであり、名前の強いアセンブリと弱い名前のアセンブリでは動作が異なります。プロセスの適切な説明として、この記事をお勧めします:Assembly LoadContextsSubtleties

この場合、MEFを使用するときは、舞台裏で同じプロセスが実行されます。

  1. .NETランタイムは、最初の弱い型のプラグインアセンブリを見つけてその場所からロードし、MEFがエクスポート処理を行います。

  2. 次に、MEFはカタログを使用して検出した次のプラグインアセンブリを処理しようとしますが、ランタイムは同じメタデータがすでにロードされているアセンブリを認識します。そのため、すでにロードされているものを使用してエクスポートを検索し、同じタイプを再度インスタンス化することになります。2番目のDLLにはまったく触れません。

ランタイムによって同じアセンブリを複数回ロードできる方法はありません。あなたがそれを考えるとき、それは完全に理にかなっています。アセンブリは、メタデータを含む一連のタイプであり、ロードされたタイプが使用可能になると、再度ロードする必要はありません。

これは完全には正しくないかもしれませんが、問題がどこにあるのかを説明するのに役立つことを願っています。この目的でDLLを複製することは役に立たないことを明確にする必要があります。

今、あなたが達成したいことに関して。必要なのは、同じアダプタプラグインの複数のインスタンスを取得して、それらをさまざまな目的に使用することだけであるようです。これは、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を使用して、何をどのように構成し、どのようにインスタンス化するかを検討することもできます。

于 2013-03-14T09:47:34.777 に答える