Managed Extensibility FrameworkからIronRubyを簡単に使う

MEF記事の連続投下第三弾。あまり一般受けしないネタを連投です。(^^;

IronRubyをアプリケーションにホスティングは難しくありませんが、dynamicを使うなど特別扱いが必要だったりするし、何よりもVS上でコードを書いてビルドという流れの中に「rubyでコードを書いてC#ホスティングコードを書く」が入ると開発のテンポが悪くなります。私的にはrubyでコードを書いたら勝手に取り込んでくれるくらいでないとIronRubyホスティングしようとは思わないです。

ということで今回のミッション。

C#で作成したインタフェースをrubyで実装してC#のクラスにインポートさせたい。

rubyで簡易的に実装して、あとでC#のコードで差し替えたりとかそんな使い方を想定しています。

必要なもの

  • RubyCodeExportProvider : rubyで書いたクラスに対するエクスポートプロバイダ。

これだけでOK.前回のAOPに比べたら実装は全然楽ですね。

続きを読む

Managed Extensibility FrameworkでAOPその2

MEFを人に説明するとき、どう言えばよいのでしょうか? 私の場合、DIコンテナを知っている人には「DIコンテナっぽい何か」と茶を濁しています。さて、DIコンテナはxmlで設定ファイルを書くものが多かった印象がありますが、MEFはカスタム属性ベースになっています。

前回動いただけで満足したメソッド割り込みを今回はMEFの流儀(?)に従ってカスタム属性で設定可能にしたいと思います。

完成形はこんなの

[Intercept(typeof(IFoo), typeof(InterceptorX))]
[Intercept(typeof(IBar), typeof(InterceptorY), typeof(InterceptorZ))]
public class Bazz : IFoo, IBar { /* ... */ }

Intercept属性を用意して、対象となるインタフェースとインターセプタを指定します。Castle.DynamicProxyだとインタフェースに割り込むのが都合が良いので、こんな感じにしています。

必要なもの

  • InterceptAttribute : 割り込み用カスタム属性
  • InterceptableExportProvider : カスタム属性からプロキシを生成するエクスポートプロバイダ

この2つがあればOK。

では早速実装しましょう。

続きを読む

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も見よう見まねで使ったので色々怪しそう・・・

近況

今年初めての日記・・・(^^;

最近、技術的に面白みのないコードしか書いていないので、プログラミングが楽しめない状況だったりします。今年はプログラミング以外の趣味に力を入れることにしたので、日記の更新頻度が低いと思います。ごめんなさい。

ホワイトボードプログラミング

プログラマーの力量を見極める--面接官になったら尋ねるべき質問実例集 - ZDNet Japan
なんか話題になっていたので。

ループを使わずに配列の順序を逆にする。

  • 再帰呼び出しはループに含めますか?→再帰呼び出しを知っていることを試したい?
  • ライブラリを使ってもよいですか?→なんでも自作するのではなく、ライブラリを活用することを考えるかどうかを試したい?
  • gotoとか使ってもよいですか?→細部にこだわりすぎ。パズル感覚で問題を解くのに夢中になりすぎかな?
  • 質問すること自体が正解かもしれない。→仕様や前提条件の確認をするかどうか試したい?

私なら、回答と一緒に上の文章を添えておくかもしれないです。

ブロブのプロパティ取得

昨日のサンプルでブロブのプロパティが取得できていなかったので、再チャレンジ。

        private void Reflesh()
        {
            var blobs = blobContainer.ListBlobs().OfType<CloudBlob>().ToList();
            // FetchAttributesを呼び出さないと属性が読み込まれない?
            blobs.ForEach(blob => blob.FetchAttributes());            
            
            Repeater1.DataSource = blobs;
            Repeater1.DataBind();
        }

色々弄ってみた結果、FetchAttributesメソッドの呼び出しが必要っぽいです。
また、表示側は、

            <ItemTemplate>
                <tr>
                    <td><img src="<%# Eval("Uri") %>" width="100" 
                        title="<%# Eval("Attributes.Metadata[Title]") %>" /></td>
                    <td><%# Eval("Properties.ContentType") %></td>
                    <td><%# Eval("Properties.Length") %></td>
                    <td><%# Eval("Properties.LastModifiedUtc") %></td>
                </tr>
            </ItemTemplate>

titleをメタデータ内のTitleに書き換えている以外は、書籍と同じ値を表示させています。

これでブロブストレージがスッキリしたので、次はテーブルストレージかな。