【问题标题】:Spring AOP + MVVM Foundation + PropertyChangedSpring AOP + MVVM 基础 + PropertyChanged
【发布时间】:2023-04-03 09:33:01
【问题描述】:

我正在使用 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,但这似乎不起作用。)

【问题讨论】:

  • 您正在定义一个“AttributeMatchMethodPointcut(typeof(AttributeStoringMethod)”但您没有使用该“AttributeStoringMethod”-属性来标记哪些方法是切入点。您可以将代码的一部分发布到您实际所在的位置吗使用那个属性?

标签: c# mvvm aop spring.net mvvm-foundation


【解决方案1】:

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

【讨论】:

  • 我不认为这是 OP 的问题。我认为 user327911 发现他的目标对象的属性设置器从未被调用,这是因为 WhoCaresItsBroke 属性不是虚拟的,因此不会被代理覆盖。这就是为什么代理从不将调用委托给目标,而是设置它自己的属性。你怎么看?
  • 你是对的,属性应该是虚拟的(就像我链接的 spring 示例一样)。此外,他必须为设置器使用“AttributeStoringMethod”-属性或以其他方式通知 ProxyFactory 切入点。
【解决方案2】:

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

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

如果您希望在触发目标 PropertyChanged 事件时在代理上触发 PropertyChanged 事件,您应该按照以下 hack 中的建议手动实现。

黑客攻击或在代理目标上触发PropertyChanged的解决方法

代理工厂不会将目标事件连接到代理上的类似事件,但您可以手动执行此操作。我不确定我是否会建议您这样做,但您可以使用以下技巧。

重写你的代理工厂和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-06-16
    • 1970-01-01
    • 1970-01-01
    • 2011-09-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-23
    相关资源
    最近更新 更多