1

我正在使用 Spring.Net 1.3.1 和 MVVM Foundation 来对我的视图模型应用横切。我注意到,如果我在对象转换为横切代理之前分配属性更改处理程序,则代理引擎不会将属性更改处理程序应用于代理。有谁知道这是否是预期的行为,如果是,是否有解决方法?

我的工厂是这样的

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

似乎 Spring 示例 Spring.AopQuickStart\src\Spring.AopQuickStart.Step6 几乎与您尝试做的事情相同(拦截属性的 [自动生成] 设置器)。您可能想查看示例的来源

于 2011-04-30T08:35:17.747 回答
0

您应该将该WhoCaresItsBroke属性声明为虚拟的,否则它不会被您的代理对象覆盖。将其设置为虚拟将导致您的处理程序pto再次被调用,因为代理会将属性调用委托给其目标。

您不需要NotifyPropertyChangedAdvice,您可以将其删除。ObservableObject您正在使用的类已经实现了所需的行为。

如果您希望在PropertyChanged触发目标事件时在代理上PropertyChanged触发事件,则应手动实现此操作,如以下 hack 中所建议的那样。

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 步:将属性声明为虚拟

因为我们已经将属性WhoCaresItsBroke设为虚拟,所以它可以在代理中被覆盖。在被覆盖的属性中,代理对象WhoCaresItsBroke将对属性的所有调用委托WhoCaresItsBroke给目标。

在此步骤之后,您将看到附加到您的pto实例的原始处理程序被调用。但是,PropertyChanged不会引发代理上的事件。

第 2 步:将目标事件挂钩到代理上的处理程序

只需将目标事件挂钩PropertyChanged到引发其自己PropertyChanged事件的代理上的处理程序。我们可以使用相同的名称,因为在代理中我们可以假设我们属于同一类型。

于 2011-05-18T15:36:52.017 回答