Spring AOP + MVVM Foundation + PropertyChanged

Я использую 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, обработчик изменения свойства, который я ранее подключил, никогда не срабатывает. (Я пытался использовать NotifyPropertyChangedAdvice, как это предусмотрено на форумах spring.net, но это не работает.)

2 ответа

Кажется, что Spring примеры Spring.AopQuickStart\src\Spring.AopQuickStart.Step6 делает почти то же самое, что вы пытаетесь сделать (перехватывая [autogenerated] сеттер свойства). Возможно, вы захотите взглянуть на источник примера.

Вы должны объявить WhoCaresItsBroke свойство как виртуальное, иначе оно не будет переопределено вашим прокси-объектом. Если вы сделаете его виртуальным, ваш обработчик pto быть вызванным снова, потому что прокси делегирует вызов свойства своей цели.

Вам не нужно NotifyPropertyChangedAdviceВы можете удалить его. Желаемое поведение уже реализовано ObservableObject класс вы используете.

Если вы хотите PropertyChanged событие, которое будет запущено на прокси, когда цель PropertyChanged событие происходит, вы должны реализовать это вручную, как предложено в следующем разделе.

Взлом или обходной путь к огню 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 событие. Мы можем использовать то же имя, потому что в прокси мы можем предположить, что мы одного типа.

Другие вопросы по тегам