kazuk は null に触れてしまった

C# / .NET 系技術ネタ縛りでお送りしております

タグアーカイブ: ComposeParts

MEFでディレクトリカタログを追いかける(C# Advent Calender jp:2010 12/02)


ってタイトルで用意してたんだけど、結果的には追加はいいけど、削除、更新は駄目だったの巻

MEF いいっすよねー。

ロジック殆ど無しのホストに Extensibility で拡張を仕込んでいければ、システムのメンテナンス単位を小さくできる。停止時間を当然に減らせる。

良い事だらけだ!

って感じなんですが、本当に拡張していけるのか試していってという感じ。

Managed Extensibility Framework の概要

MEF では発見可能性、拡張性、および移植性に重点が置かれているのに対し、MAF では拡張機能の特定とアセンブリの読み込みおよびアンロードに重点が置かれています。

InfoQ: CLRが、何回目かの、「最初の」プラグインモデルを採用。

  • System.Addin は、使用されなくなったAppDomainを自動的に、アンロードし、メモリを再利用できるように、管理している。

 

MAF使わないとアンロードできないのか…

そんな事は無い、アセンブリのシャドウコピーがあるじゃないか!

って試したコードが以下。

using System;
using System.Collections.Generic;
using System.IO;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;

namespace MefConsoleApplication
{
    public interface IDoSomething
    {
        void DoSomethig();
    }

    public class Program
    {

#pragma warning disable 649
        [ImportMany(AllowRecomposition = true)]
        private IEnumerable<Lazy<IDoSomething>> _parts;
#pragma warning restore 649

        private CompositionContainer _container;
        private FileSystemWatcher _fsWatcher;
        private DirectoryCatalog _directoryCatalog;

        public void Run()
        {
            AggregateCatalog aggregateCatalog = new AggregateCatalog();
            aggregateCatalog.Catalogs.Add(
                new AssemblyCatalog(typeof(Program).Assembly));
            // ReSharper disable AssignNullToNotNullAttribute
            string extentionPath = Path.Combine(
                Path.GetDirectoryName(typeof(Program).Assembly.Location),
                "..\\..\\Extentions");
            // ReSharper restore AssignNullToNotNullAttribute
            _fsWatcher = new FileSystemWatcher(extentionPath);
            _fsWatcher.Changed += FsWatcherChanged;
            _fsWatcher.EnableRaisingEvents = true;

            _directoryCatalog = new DirectoryCatalog(
                extentionPath);
            _directoryCatalog.Changed += CatalogChanged;
            aggregateCatalog.Catalogs.Add(
                _directoryCatalog);
            aggregateCatalog.Changed += CatalogChanged;
            _container = new CompositionContainer(aggregateCatalog);
            _container.ComposeParts(this);

            foreach (var part in _parts)
            {
                part.Value.DoSomethig();
            }

            while (Console.ReadLine() != "exit")
            {
            }
        }

        void FsWatcherChanged(object sender, FileSystemEventArgs e)
        {
            Console.WriteLine("extentions directory changed");
            _directoryCatalog.Refresh();
        }

        void CatalogChanged(object sender, ComposablePartCatalogChangeEventArgs e)
        {
            Console.WriteLine("catalog changed " + sender.GetType().Name);
            _container.ComposeParts(this);

            foreach (var part in _parts)
            {
                part.Value.DoSomethig();
            }
        }

        static void Main()
        {
            if (!AppDomain.CurrentDomain.ShadowCopyFiles)
            {
                Console.WriteLine("ShadowCopyFilesがオフです");
                AppDomainSetup appDomainSetup = new AppDomainSetup();
                appDomainSetup.ShadowCopyFiles = "true";
                appDomainSetup.ShadowCopyDirectories = Path.GetTempPath();
                appDomainSetup.CachePath = Path.GetTempPath();

                AppDomain execDomain = AppDomain.CreateDomain("execDomain",
                    AppDomain.CurrentDomain.Evidence, appDomainSetup);
                execDomain.ExecuteAssembly(typeof(Program).Assembly.Location);
                return;
            }
            Console.WriteLine("ShadowCopyFilesがオンです");

            Program prog = new Program();
            prog.Run();
        }
    }

    [Export(typeof(IDoSomething))]
    public class ImplSomething1 : IDoSomething
    {
        public void DoSomethig()
        {
            Console.WriteLine("do something 1");
        }
    }
}

 

Main での AppDomainSetup を介して自分を実行し直すって方法で、アプリケーションドメインをシャドウコピー可能にしている。

using System;
using System.ComponentModel.Composition;
using MefConsoleApplication;

namespace ApplicationComponent1
{
    [Export(typeof(IDoSomething))]
    public class Class1 : IDoSomething
    {
        public void DoSomethig()
        {
            Console.WriteLine("extention component1");
        }
    }
}

 

ってアセンブリを放り込むと、ディレクトリの変更を検出して、ちゃんとアセンブリがロードされる。大成功だ。

20101203192208

で、放り込んだもんを削除しようとする。

20101203192308

残念!ってわけで拒否されてしまいました…。

MEF で DirectoryCatalog のChangeを追うには以下が注意事項でございます。

  1. FileSystemWatcherで監視してないと駄目、_directoryCatalog.Refresh()でカタログを更新する必要がある。
  2. [ImportMany(AllowRecomposition = true)] 重要、AllowRecompositionをtrueにしないとRejectされる。
  3. シャドウコピーを有効にしてもアセンブリの削除はできないぜ。

 

って訳で、長い!なMEFでディレクトリカタログを追ってみたけど失敗した!な記事でございます。

C# Advent Calendar jp: 2010 : ATND の 12/2日分って事で次の日の人よろしく。

有用?よくわかんない記事で済まん。