Managed Extensibility FrameworkでAOP

個人的に.NET4で一番期待していたMEFですが、何か足りないなぁっと思っていたらAOPがサポートされていなかったんですね。twitterでつぶやいていた案でちょっと組んでみました。ExportProviderでごにょごにょ。ただ、まだ勉強のためのコードなのでこのまま仕事とかでは使えないと思います。(^^;

using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.Linq;
using System.Reflection;
using System.ComponentModel.Composition.Primitives;
using Castle.Core.Interceptor;
using Castle.DynamicProxy;

namespace MEFAopSample
{
    // テスト用のインタフェース
    public interface IGreeting
    {
        void Say(string msg);
    }

    // 簡単なインタフェースの実装
    // MEFでエクスポート
    [Export(typeof(IGreeting))]
    public class Greeting : IGreeting
    {
        public void Say(string msg)
        {
            Console.WriteLine(msg);
        }
    }

    // インターセプタ
    public class SimpleInterceptor : IInterceptor
    {
        public void Intercept(IInvocation invocation)
        {
            // メソッドの呼び出しの前後にメッセージを挟むだけの簡単な割り込み
            Console.WriteLine("Begin intercept...");
            invocation.Proceed();
            Console.WriteLine("End intercept.");
        }
    }

    // AOPを組み込んだエクスポートプロバイダ
    public class AopExportProvider : CatalogExportProvider
    {
        private readonly ProxyGenerator _generator = new ProxyGenerator();
        private readonly Dictionary<Type, IInterceptor> _dic = new Dictionary<Type, IInterceptor>();

        public void RegisterProxy(Type type, params IInterceptor interceptors)
        {
            _dic[type] = interceptors;
        }

        public AopExportProvider(ComposablePartCatalog catalog) : base(catalog)
        {
        }

        public AopExportProvider(ComposablePartCatalog catalog, bool isThreadSafe) : base(catalog, isThreadSafe)
        {
        }

        private object CreateProxy(object target)
        {
            foreach (Type key in _dic.Keys)
            {
                if (!key.IsInstanceOfType(target))
                    continue;
                return _generator.CreateInterfaceProxyWithTarget(key, target, _dic[key]);
            }

            return target;
        }

        protected override IEnumerable<Export> GetExportsCore(ImportDefinition definition, AtomicComposition atomicComposition)
        {
#pragma warning disable 1911
            return from export in base.GetExportsCore(definition, atomicComposition)
                   let export1 = export
                   select new Export(export.Definition, () => CreateProxy(export1.Value));
        }
    }
    
    [Export]
    public class Program
    {
        // MEFでインポートされる
        [Import]
        public IGreeting Greeting { get; set; }

        static void Main(string args)
        {
            var catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
            var ep = new AopExportProvider(catalog);
            // 対象となるインタフェースとインターセプタを登録
            ep.RegisterProxy(typeof (IGreeting), new SimpleInterceptor());

            var container = new CompositionContainer(ep);
            ep.SourceProvider = container;

            var program = container.GetExportedValue<Program>();
            program.Run();
        }

        public void Run()
        {
            Greeting.Say("Hello, World.");
        }
    }
}

/* 結果
Begin intercept...
Hello, World.
End intercept.
 */

一応は動いているみたいですが、Castle.DynamicProxyも見よう見まねで使ったので色々怪しそう・・・