続々・プロクシを使ってAOP(singleton編)

あの使いづらいMSDNドキュメントと格闘し(しかもOnline)メソッドの引数にアクセスすることが可能になった。
というか原因は「IMethodCallMessage」を使ってたこと。
よくよくみれば、このインタフェースはgetアクセッサしか提供してないじゃんorz
ならば実装クラスがあるはずとみていくと「MethodCallMessageWrapper」を発見。
プロパティを確認するとちゃんとget/setアクセッサがあるw
こいつのArgsプロパティ経由でメソッド引数の操作ができた。
あと、ついでにメソッドの実行をリモートサービス経由ではなくリフレクション使うように変えてみた。
これでMarshalByRefObjectから解放されるかなとおもったけど、そもそもRealProxy使う時点で無理なのね。
そんなわけで以下ソース。

using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Messaging;
using System.Reflection;

namespace AopProxySample.Aop.Impl
{
    public class MethodInvocation : IMethodInvocation
    {
        private object _target;
        private MethodCallMessageWrapper _message;

        public object Target
        {
            get { return _target; }
        }
        public IMethodCallMessage Message
        {
            get { return _message; }
        }
        public MethodInfo Method
        {
            get { return (MethodInfo)_message.MethodBase; }
        }
        public object[] Arguments
        {
            get { return _message.Args; }
            set { _message.Args = value; }
        }

        public MethodInvocation(object target, IMessage message)
        {
            this._target = target;
            this._message = new MethodCallMessageWrapper((IMethodCallMessage)message);
        }

        public object Proceed()
        {
            object res = Method.Invoke(Target, Arguments);
            return new ReturnMessage(res, Message.Args, Message.Args.Length, Message.LogicalCallContext, Message);
        }
    }
}

ついでにファクトリクラスで生成するインスタンスをキャッシュするように変更。
ステートレスになる代わりにコストは若干よくなる?
(ずっとメモリ上に置いておくのもどうなんだろ)

namespace AopProxySample.Aop.Proxies
{
    public class AopProxyFactory : RealProxy
    {
        // インスタンス化したクラスオブジェクトのキャッシュ
        private static readonly Dictionary<string, object> ClzzCache = 
            new Dictionary<string, object>();

        public static T CreateInstance<T>() where T : MarshalByRefObject
        {
            if (ClzzCache.ContainsKey(typeof(T).FullName))
            {
                return (T)ClzzCache[typeof(T).FullName];
            }
            else
            {
                T target = Activator.CreateInstance<T>();
                AopProxyFactory proxy = new AopProxyFactory(typeof(T));
                object transProxy = proxy.GetTransparentProxy();
                ClzzCache.Add(typeof(T).FullName, (T)transProxy);
                return (T)transProxy;
            }
        }
    }
}

これで個人的AOPファクトリでやりたいことはできたっぽい。
たぶん、がんばればトランザクション管理とかもできる…はず?w


追記
これ今のままだと属性ふたつつけたら対象メソッドが2回実行されるじゃんorz
プロクシのInvokで属性取得ループのせいか…。
となると属性のネスト(?)を伝播させないとダメてことだから、IMethodInvocation.Proceed()あたりでなにかしら手段講じないとダメかもしんない。
あ゛〜めんどくさくなってきた。