Managed Extensibility Framework(MEF)入門 -DirectoryCatalog-

MEFではコンテナーにある領域のパーツを取り込む為の機構として「カタログ」と言う物が用意されています。今回は「あるディレクトリにあるアセンブリパーツを取り込む」為のカタログ『System.ComponentModel.Composition.Hosting.DirectoryCatalog』クラスを取り上げます。DirectoryCatalogクラスはMSDNでこのように説明されています。


DirectoryCatalog は、指定されたディレクトリの内容を解析します。DLL ファイルに含まれている属性付きパーツは抽出され、カタログで利用可能になります。特定の DLL と解析を制限する検索パターンは、GetFiles と同じ構文を使用して指定することもできます


DirectoryCatalogはコンストラクタで指定されたパスを元に、そのパス内に存在しているアセンブリよりパーツを検出します。そのDirectoryCatalogクラスをコンテナーのコンストラクタ引数に指定するとコンテナーはカタログよりパーツを取得しコンテナー内に構成します。

 

directorycatalog_details

 

簡単なサンプルコードを元に見てみましょう。

C# DirectoryCatalog.CSharpプロジェクト
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
 
namespace DirectoryCatalogSample.CSharp
{
    public class Program
    {
        /// <summary>
        /// コンテナー
        /// </summary>
        private CompositionContainer container;
 
        /// <summary>
        /// コンストラク
        /// </summary>
        private Program()
        {
            DirectoryCatalog catalog = new DirectoryCatalog(@"C:\Parts.CSharp");
            this.container = new CompositionContainer(catalog);
        }
 
        /// <summary>
        /// View群
        /// </summary>
        [ImportMany()]
        public IEnumerable<IView> Views { get; set; }
 
        /// <summary>
        /// 実行処理
        /// </summary>
        private void Run()
        {
            //コンテナーより直接サービスを取得する。
            IEnumerable<IView> views = this.container.GetExportedValues<IView>();
            foreach (IView view in views)
            {
                Console.WriteLine(string.Format("タイトルは[{0}]です。", view.GetTitle()));
            }
 
            //自身のインスタンスにサービスをインポートする。
            this.container.ComposeParts(this);
            foreach (IView view in this.Views)
            {
                Console.WriteLine(string.Format("タイトルは[{0}]です。", view.GetTitle()));
            }
        }
 
        /// <summary>
        /// エントリポイント
        /// </summary>
        /// <param name="args"></param>
        public static void Main(string[] args)
        {
            Program program = new Program();
            program.Run();
        }
    }
 
    /// <summary>
    /// IViewインターフェース
    /// </summary>
    public interface IView
    {
        string GetTitle();
    }
}
C# ViewPartsプロジェクト(C:\Parts.CSharpに出力している)
using System;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using DirectoryCatalogSample.CSharp;
 
namespace ViewParts.CSharp
{
    /// <summary>
    /// メインビュー
    /// </summary>
    [Export(typeof(IView))]
    public class MainView : IView
    {
        public string GetTitle()
        {
            return "Main Window";     
        }
    }
 
    /// <summary>
    /// メッセージダイアログビュー
    /// </summary>
    [Export(typeof(IView))]
    public class MessageDialog : IView
    {
        public string GetTitle()
        {
            return "Message Dialog";
        }
    }
}
VB DirectoryCatalogSample.VBプロジェクト
Imports System
Imports System.ComponentModel.Composition
Imports System.ComponentModel.Composition.Hosting
 
Public Class Program
    ''' <summary>
    ''' コンテナー
    ''' </summary>
    Private container As CompositionContainer
 
    ''' <summary>
    ''' View群
    ''' </summary>
    <ImportMany()> _
    Public Property Views As IEnumerable(Of IView)
 
    ''' <summary>
    ''' コンストラク
    ''' </summary>
    Private Sub New()
        Dim catalog As New DirectoryCatalog("C:\Parts.VB")
        Me.container = New CompositionContainer(catalog)
    End Sub
 
    ''' <summary>
    ''' 処理実行
    ''' </summary>
    Private Sub Run()
        'コンテナーより直接サービスを取得する。
        Dim views As IEnumerable(Of IView) = Me.container.GetExportedValues(Of IView)()
        For Each view As IView In views
            Console.WriteLine(String.Format("タイトルは[{0}]です。", view.GetTitle()))
        Next
 
        '自身のインスタンスにサービスをインポートする。
        Me.container.ComposeParts(Me)
        For Each view As IView In Me.Views
            Console.WriteLine(String.Format("タイトルは[{0}]です。", view.GetTitle()))
        Next
    End Sub
 
    ''' <summary>
    ''' エントリポイント
    ''' </summary>
    Public Shared Sub Main()
        Dim program As New Program()
        program.Run()
    End Sub
End Class
 
''' <summary>
''' IViewインターフェース
''' </summary>
Public Interface IView
    Function GetTitle() As String
End Interface
VB ViewParts.VBプロジェクト(C:\Parts.VBに出力している)
Imports System
Imports System.ComponentModel.Composition
Imports System.ComponentModel.Composition.Hosting
Imports DirectoryCatalogSample.VB
 
''' <summary>
''' メインビュー
''' </summary>
<Export(GetType(IView))> _
Public Class MainView
    Implements IView
    Public Function GetTitle() As String Implements DirectoryCatalogSample.VB.IView.GetTitle
        Return "Main Window"
    End Function
End Class
 
''' <summary>
''' メッセージダイアログ
''' </summary>
<Export(GetType(IView))> _
Public Class MessageDialog
    Implements IView
    Public Function GetTitle() As String Implements DirectoryCatalogSample.VB.IView.GetTitle
        Return "Message Dialog"
    End Function
End Class

サンプルコードを実行すると以下のように出力されます。

directorycatalog_details_output

 

サンプルコードでは、実行アプリケーションとして「DirectoryCatalogSample.Charp / DirectoryCatalogSample.VB」プロジェクトを作成し、エクスポートインターフェースのIViewインターフェースを定義しています。又、パーツ群を定義した「ViewParts.CSharp / ViewParts.VB」プロジェクトを別途作成し、コンパイル後の出力先を「C:\Parts.Charp / C:\Parts.VB」に出力しています。DirectoryCatalogクラスはコンストラクタで渡されたパスを元にアセンブリを検索し、見つかったアセンブリ内より指定されたパーツを検索しにいきます。コンテナーはDirectoryCatalogクラスより検索されたパーツを取得し、構築して提供します。DirectoryCatalogクラスを使用すると、アプリケーションの実行ディレクトリ以外の場所で配置されているアセンブリを動的に読む事が出来ます。この機構を使用する事で、アプリケーション実行時であっても動的にパーツを取得し使用したりする事が出来ます。DirectoryCatalogクラスはとても使い勝手が良くお気に入りのCatalogクラスです。是非動的なアセンブリの取得をMEFで試してみては如何でしょうか。

 

Source code