1

私はSpring.Net1.3.1をMVVMFoundationと一緒に使用して、ビューモデルにクロスカットを適用しています。オブジェクトがクロスカットのためにプロキシに変換される前にプロパティ変更ハンドラーを割り当てると、プロキシエンジンがプロパティ変更ハンドラーをプロキシに適用しないことに気付きました。これが予想される動作であるかどうか、もしそうであれば、回避策があるかどうかを誰かが知っていますか?

私の工場はこんな感じ

public static class AopProxyFactory {
    public static object GetProxy(object target) {
        var factory = new ProxyFactory(target);

        factory.AddAdvisor(new Spring.Aop.Support.DefaultPointcutAdvisor(
                                new AttributeMatchMethodPointcut(typeof(AttributeStoringMethod)),
                                new UnitValidationBeforeAdvice())
                           );

        factory.AddAdvice(new NotifyPropertyChangedAdvice());
        factory.ProxyTargetType = true;

        return factory.GetProxy();
    }
}

アドバイスはこんな感じ

    public class UnitValidationBeforeAdvice : IMethodBeforeAdvice {
    public UnitValidationBeforeAdvice() {            
    }

    public void Before(MethodInfo method, object[] args, object target) {
        if (args.Length != 1) {
            throw new ArgumentException("The args collection is not valid!");
        }

        var canConvertTo = true;
        if (!canConvertTo) {
            throw new ArgumentException("The '{0}' cannot be converted.");
        }
    }
}

public class NotifyPropertyChangedAdvice : IAfterReturningAdvice, INotifyPropertyChanged {
    public event PropertyChangedEventHandler PropertyChanged;

    public void AfterReturning(object ReturnValue, MethodInfo Method, object[] Args, object Target) {
        if (Method.Name.StartsWith("set_")) {
            RaisePropertyChanged(Target, Method.Name.Substring("set_".Length));
        }
    }

    private void RaisePropertyChanged(Object Target, String PropertyName) {
        if (PropertyChanged != null)
            PropertyChanged(Target, new PropertyChangedEventArgs(PropertyName));
    }
}

プロキシしているオブジェクトは次のようになります

    public class ProxyTypeObject : ObservableObject {
    private string whoCaresItsBroke;
    public string WhoCaresItsBroke {
        get { return whoCaresItsBroke; }
        set {
            whoCaresItsBroke = value;
            RaisePropertyChanged("WhoCaresItsBroke");
        }
    }
}

そして、呼び出しコード

var pto = new ProxyTypeObject();
                pto.WhoCaresItsBroke = "BooHoo";
                pto.PropertyChanged += (object sender, System.ComponentModel.PropertyChangedEventArgs e) => {
                    return;
                };

                var proxy = AopProxyFactory.GetProxy(pto);
                (proxy as ProxyTypeObject).WhoCaresItsBroke = "BooHoo2";

「WhoCaresItsBroke」プロパティを設定すると、以前に接続したプロパティ変更ハンドラーがヒットしないことに気付くでしょう。(spring.netフォーラムで提供されているNotifyPropertyChangedAdviceを使用してみましたが、機能していないようです。)

4

2 に答える 2

0

プロパティを仮想として宣言する必要がありWhoCaresItsBrokeます。そうしないと、プロキシ オブジェクトによってオーバーライドされません。仮想化するとpto、プロキシがプロパティ呼び出しをそのターゲットに委任するため、ハンドラーが再度呼び出されます。

は必要ありませんNotifyPropertyChangedAdvice。削除できます。ObservableObject目的の動作は、使用しているクラスによって既に実装されています。

PropertyChangedターゲット イベントが発生したときにプロキシでイベントを発生させたい場合PropertyChangedは、次のハックで提案されているように、これを手動で実装する必要があります。

PropertyChangedプロキシターゲットで起動するハックまたは回避策

proxyfactory は、ターゲット イベントをプロキシ上の同様のイベントに接続しませんが、これは手動で行うことができます。そうするようにアドバイスするかどうかはわかりませんが、次のハックを使用できます。

プロキシ ファクトリを書き換えて、次のようにしProxyTypeObjectます。

public class ProxyTypeObject : ObservableObject
{
    private string whoCaresItsBroke;
    // step 1:
    // make the property virtual, otherwise it will not be overridden by the proxy
    public virtual string WhoCaresItsBroke
    {
      // original implementation
    }

    public void PublicRaisePropertyChanged(string name)
    {
        RaisePropertyChanged(name);
    }
}

public static class AopProxyFactory
{
    public static object GetProxy(object target)
    {
        ProxyFactory factory = GetFactory(target);

        object proxy = factory.GetProxy();

        if(target is ProxyTypeObject)
        {
            // step 2:
            // hack: hook handlers ...
            var targetAsProxyTypeObject = (ProxyTypeObject)target;
            var proxyAsProxyTypeObject = (ProxyTypeObject)proxy;
            HookHandlers(targetAsProxyTypeObject, proxyAsProxyTypeObject);
        }

        return proxy;

    }

    private static void HookHandlers(ProxyTypeObject target, ProxyTypeObject proxy)
    {
        target.PropertyChanged += (sender, e) =>
        {
            proxy.PublicRaisePropertyChanged(e.PropertyName);
        };
    }

    private static ProxyFactory GetFactory(object target)
    {
        var factory = new ProxyFactory(target);
        // I simply add the advice here, but you could useyour original
        //  factory.AddAdvisor( ... )
        factory.AddAdvice(new UnitValidationBeforeAdvice());
        // You don't need this:
        // factory.AddAdvice(new NotifyPropertyChangedAdvice()); 
        factory.ProxyTargetType = true;
        return factory;
    }
}

これには、 ;ProxyTypeObjectを発生させる公開メソッドが必要です。PropertyChangedEventおそらくこれを別の方法で行う必要がありますが、それは重要ではありません。

使い方

ProxyTypeObjectを設定したため、ファクトリはタイプ のプロキシを返しますfactory.ProxyTargetType = true;。ただし、これは依然として構成ベースのプロキシです。プロキシ化すると、元のオブジェクト (ターゲット)新しいプロキシ オブジェクトが得られます。プロキシとターゲットの両方がタイプであり、イベントProxyTypeObjectを発生させることができます。PropertyChanged

この段階でWhoCaresItsBroke、プロキシに設定すると、PropertyChangedイベントはプロキシで発生しますが、ターゲットでは発生しません。ターゲット プロパティは変更されません。

ステップ 1: プロパティを仮想として宣言する

プロパティをWhoCaresItsBrokevirtual にしたため、プロキシでオーバーライドできます。WhoCaresItsBrokeオーバーライドされたプロパティでは、プロキシ オブジェクトはプロパティへのすべての呼び出しをWhoCaresItsBrokeターゲットに委任します。

ptoこのステップの後、インスタンスにアタッチした元のハンドラーが呼び出されることがわかります。ただし、PropertyChangedプロキシのイベントは発生しません。

ステップ 2: ターゲット イベントをプロキシのハンドラーにフックする

PropertyChanged独自のイベントを発生させるプロキシ上のハンドラーにターゲット イベントをフックするだけPropertyChangedです。プロキシでは同じタイプであると想定できるため、同じ名前を使用できます。

于 2011-05-18T15:36:52.017 に答える
0

Spring の例 Spring.AopQuickStart\src\Spring.AopQuickStart.Step6 は、あなたがしようとしているのとほぼ同じことを行うようです (プロパティの [自動生成された] セッターをインターセプトします)。例のソースを見たいと思うかもしれません。

于 2011-04-30T08:35:17.747 に答える