2011-04-29 83 views
1

我使用Spring.Net 1.3.1與MVVM Foundation一起對我的viewmodels應用交叉切割。我注意到,如果我在將對象轉換爲交叉代理之前分配屬性更改的處理程序,則代理引擎不會將屬性更改的處理程序應用於代理。有誰知道這是否是預期的行爲,如果有,是否有解決方法?Spring AOP + MVVM Foundation + PropertyChanged

我廠是這樣

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,但看起來不起作用。)

+1

您正在定義一個「AttributeMatchMethodPointcut(typeof(AttributeStoringMethod)」,但您並未使用該「AttributeStoringMethod」-Attribut來標記哪些方法是切入點。是否可以將實際使用該屬性的代碼部分發布到其他位置? – tobsen 2011-04-29 22:02:21

回答

0

看來,Spring的例子Spring.AopQuickStart \ src \ Spring.AopQuickStart.Step6幾乎和你一樣試圖做(攔截一個Property的[自動生成] setter)。 你可能想看看the source of the example

+1

我認爲這不是OP的問題,我認爲user327911發現他的目標對象的屬性設置器從來沒有被調用,這是因爲WhoCaresItsBroke屬性不是虛擬的,因此不會被代理覆蓋,這就是爲什麼代理不會將呼叫委託給目標,而是將其設置爲自己的屬性。您認爲如何? – Marijn 2011-05-23 13:54:08

+0

您說得對,該屬性應該是虛擬的(就像在我鏈接的彈簧示例中一樣),並且必須使用「AttributeStoringMethod 「 - 爲制定者分配或以其他方式通知ProxyFactory關於切入點的信息。 – tobsen 2011-05-23 16:17:59

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事件代理的處理程序。我們可以使用相同的名稱,因爲在代理中我們可以假設我們的類型相同。