2010-09-13 70 views
3

我正在重溫我去年寫過的課程跟蹤(髒邏輯)。目前,我有一個超級基礎類來處理所有狀態跟蹤,但每個需要跟蹤的屬性需要遵循標準的get { return _x; } set { _isDirty = true; _x = value; }工作方式。如何動態實現代理模式?

玩過實體框架並閱讀Proxy Pattern後,我希望有更好的方法來實現我的IsDIrty邏輯,同時能夠使用自動實現的屬性?

說實話,我不知道我在說什麼。有沒有一種方法可以讓我這樣做如下:

public class Customer : ITrackable 
{ 
    [TrackState(true)] // My own attribute 
    public virtual string Name { get;set;} 

    [TrackState(true)] 
    public virtual int Age { get;set;} 

    // From ITrackable 
    public bool IsDirty { get; protected set; } 

} 

,然後實現它會使用反射(或另一種神奇的解決方案)調用另一個方法先用TrackState的屬性設置值之前的動態代理屬性。

很顯然,我可以很容易地通過創建phyiscal代理類做到這一點,使用IOC:

public class CustomerProxy : Customer 
{ 
    Customer _customer; 

    public override string Name 
    { 
     get { return _customer.Name; } 
     set { IsDirty = true; return _customer.Name; } 
    } 

    // Other properties 
} 

但我不喜歡不必爲每個對象做到這一點,否則就沒有從我的現有解決方案的好處。希望有人能夠滿足我的好奇心,或者至少告訴我EF如何實現它。

+0

http://joe.truemesh.com/blog//000181.html – Bozho 2010-09-13 16:44:51

+1

完全不相關:殺手的用戶名,我喜歡它,哈哈。 – TheXenocide 2011-05-31 21:31:59

回答

2

城堡的DynamicProxy正是這樣做的:http://www.castleproject.org/dynamicproxy/index.html

允許您提供一個攔截器:

public void Intercept(IInvocation invocation) 
{ 
    // Call your other method first... then proceed 
    invocation.Proceed(); 
} 

您可以訪問通過invocation.Method的MethodInfo對象。您可以通過設置invocation.ReturnValue來覆蓋返回值。你可以訪問(並覆蓋)參數。

2

PostSharp可以提供幫助。或者如果你有這種感覺,你可以爲此編寫自己的IL-rewriter。 Mono.Cecil是一個偉大的圖書館,它會讓它變得輕而易舉。這裏是快速藥汁:

class Program { 

    static ModuleDefinition _module; 

    static void Main(string[] args) { 
    // the argument is the assembly path 
    _module = ModuleDefinition.ReadModule(args[0]); 
    var trackables = _module.Types. 
     Where(type => type.Interfaces.Any(tr => tr.Name == "ITrackable")); 
    var properties = trackables.SelectMany(type => type.Properties); 
    var trackableProperties = properties. 
     Where(property => property.CustomAttributes. 
     Any(ca => ca.Constructor.DeclaringType.Name == "TrackStateAttribute")); 
    trackableProperties. 
     Where(property => property.SetMethod != null). 
     ToList(). 
     ForEach(property => CallIsDirty(property.SetMethod)); 
    _module.Write(args[0]); 
    } 

    private static void CallIsDirty(MethodDefinition setter) { 
    Console.WriteLine(setter.Name); 

    var isDirty = setter.DeclaringType.Methods. 
     Single(method => method.Name == "set_IsDirty"); 
    var reference = new MethodReference(isDirty.Name, 
     _module.Import(typeof(void))) { 
     DeclaringType = setter.DeclaringType, 
     HasThis = true, 
     CallingConvention = MethodCallingConvention.Default 
     }; 
    reference.Parameters.Add(new ParameterDefinition(
     _module.Import(typeof(bool)))); 
    var IL = setter.Body.GetILProcessor(); 
    var param0 = IL.Create(OpCodes.Ldarg_0); 
    var param1 = IL.Create(OpCodes.Ldc_I4_1); 
    var call = IL.Create(OpCodes.Call, reference); 
    IL.InsertBefore(setter.Body.Instructions[0], call); 
    IL.InsertBefore(setter.Body.Instructions[0], param1); 
    IL.InsertBefore(setter.Body.Instructions[0], param0); 
    } 
} 

它用這些助手:

public class TrackStateAttribute : Attribute { } 

public interface ITrackable { bool IsDirty { get; } } 

示例代碼:

public class Customer : ITrackable { 
    [TrackState] public string Name { get; set; } 
    [TrackState] public int Age { get; set; } 
    public bool IsDirty { get; protected set; } 
} 

它假定IsDirty財產也將有一個二傳手。

+0

+1謝謝!我的項目中已經有'Castle.Core',所以我使用DynamicProxy而不是這個。 – GenericTypeTea 2010-09-14 09:22:08