LoginSignup
20
22

More than 5 years have passed since last update.

C#のプラグイン機構MEF2に関するメモ

Last updated at Posted at 2015-07-05

最近のMEF

http://blog.slaks.net/2014-11-16/mef2-roslyn-visual-studio-compatibility/
によるとMetroで使いたいという要請からMEF2が生まれた。MEF3もあるよ(まだ内部利用)。

MEF2がMEFに比べて何が嬉しいのかについては
http://blogs.microsoft.co.il/bnaya/2013/01/06/mef-20-toc/
に紹介してあった。
個々の記事が短いので読みやすいです。
それによるとGenericsできるよ。じゃぁ、MEF2だ。

しかし・・・
MEF2で見つかったサンプルは、自分でExportして自分でImportする自作自演型のものばかりで
別のdllにわけたPluginから使うにはどうするのかがなかなかわからなかった。
さらに、MEF2ですべての要素がMEFに置き換わったわけではなく、残っているけど無効にされていると思しきメソッドがあって落とし穴だらけだった。なんとか動くようになったのでメモを残します。

実装

最初に、
MEFによるC#アドインの作成を参考にMEFの動作確認をしてそこからMEF2向けに改造した。

ソリューションは、インタフェース定義のAddinContract, プラグイン実装のAddinA, プラグインをホストするMEFSampleの3プロジェクト構成。

AddinContractライブラリ

シンプル。参照の追加など何もいらない。

IAddinContract.cs
using System.Collections.Generic;

namespace AddinContract
{
    public interface IAddinContract
    {
        string AddinTitle { get; }
        void DoWork();
    }
}

AddinAプラグインライブラリ

プラグイン実装。AddinContractライブラリを参照に加える。
MEF2の力により[Export(typeof(IAddinContract))]が不要になったので、
MEF関連への参照を追加する必要はない。

AddinA
using AddinContract;

namespace AddinA
{
    public class AddinSampleA : IAddinContract
    {
        public string AddinTitle { get { return "Addin - A"; } }
        public void DoWork() { return; }
    }
}

これをビルドして、
AddinA.dllを
MEFSampleプロジェクトのbin/DEBUG/addinsディレクトリにコピーする。

MEFSampleプログラム(コンソール)

いよいよ問題のプラグインを使う方の実装です。
まず、nugetでMicrosoft.Compositionをインストールする。

PluginHostクラス

PluginHost
    public class PluginHost
    {
        //[System.Composition.ImportMany]
        [System.ComponentModel.Composition.ImportMany]
        public List<IAddinContract> Addins
        {
            get;
            set;
        }
    }

第1の罠、MEF2の属性のネームスペースが違うw

Pluginを読み込んでPluginHostに実体化させる

いくつかの工程に分かれる。
最初にMEF2のAPI上の特徴のRegistrationBuilderを作成して、必要なものをExportする。
次に、Exportされたものが入っているAssemblyからAssemblyCatalogを作る。
ここはMEFの様式だ。だけど、MEF2では使えなくなったと思しきものがある。
最後に、Containerを作って実体化させる工程。これもMEFの様式。だけど、MEF2ではw

RegistrationBuilder

RegistrationBuilderこそがMEF2の象徴。これが無いものはMEFであり、あるものはMEF2である。
こいつがAssemblyからclassやinterfaceを検索する。
なので検索できるように事前に情報をExportしてあげる必要がある。
入れるもの(Plugin, Exportする人)と入れ物(PluginのList, Importする人)の両方を登録する。

エクスポートの登録
var builder = new System.ComponentModel.Composition.Registration.RegistrationBuilder();

// addins
builder
    .ForTypesDerivedFrom<IAddinContract>() // 対象のインタフェースを指定
    //.Export<IAddinContract>() // 個別指定。これでもOK
    .ExportInterfaces()
    ;

// host
builder
        .ForType<PluginHost>() // 対象のクラスを指定
        //.ImportProperty(x => x.Addins, b => b.AsMany()) // 入れ物のプロパティを指定。ImportMany属性かこれかどっちかでいい
        .Export<PluginHost>()
        ;

AssemblyCatalog

ここの情報が見つからなくて苦労。
MEF2ではDirectoryCatalogは死んだ。ぽい。

var hostCatalog = new System.ComponentModel.Composition.Hosting.AssemblyCatalog(typeof(PluginHost).Assembly, builder);
var aCataog = new System.ComponentModel.Composition.Hosting.AggregateCatalog(hostCatalog);

#if false
var catalog = new System.ComponentModel.Composition.Hosting.DirectoryCatalog("addins");
aCataog.Catalogs.Add(catalog);
#else
foreach (var f in new DirectoryInfo("addins").GetFiles().Where(x => x.Name.ToLower().EndsWith(".dll")))
{
    var catalog = new System.ComponentModel.Composition.Hosting.AssemblyCatalog(Assembly.LoadFile(f.FullName), builder);
    aCataog.Catalogs.Add(catalog);
}
#endif

CompositionContainer

MEF2ではComposePartsは死んだ。ぽい

var container = new System.ComponentModel.Composition.Hosting.CompositionContainer(aCataog);
#if false
var host = new PluginHost();
container.ComposeParts(host);
return host;
#else
return container.GetExportedValue<PluginHost>();
#endif

メモ

MEE2システムには、3つの役割がある。
プラグイン、プラグインのホスト、プラグインを使うプログラムである。

参照関係は大雑把ににこんな感じ。
プラグインの方は特定の
interfaceを実装する以外に気にすることはない。

本体の方は、忘れずにpluginhostの情報をAssemblyCatalogに追加するべし。

+-- Exe ----------------+--> Microsoft.Composition
|                       |
| +----+---> pluginhost |
| |User|                |--> IPlugin.dll
| +----+                |     ^
|                       |     |
+-----------------------+     |
                              |
+-- Plugin.dll --+------------+
| Plugin         |
+----------------+

まとめ

  • MEF2ではGenericsが使える
  • MEF2では属性なしで、コードで指定することができる
  • MEF2でも属性は使えるが、同じ名前でMEF2のnamespaceにある属性を使うべし
  • AssemblyCatalogはAggregateCatalogに全部放り込む

以上、MEF2のメモでした。

20
22
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
20
22