如何动态实现代理模式?

时间:2010-09-13 16:43:24

标签: design-patterns c#-4.0 proxy-pattern

我正在重温我去年写的班级跟踪(脏逻辑)。目前我有一个处理所有状态跟踪的超级基类,但我需要跟踪的每个属性需要遵循标准的get { return _x; } set { _isDirty = true; _x = value; }工作方式。

在玩实体框架并阅读Proxy Pattern后,我希望有更好的方法来实现我的IsDIrty Logic,同时能够使用自动实现的属性?

说实话,我不知道我在说什么。有没有办法可以做以下事情:

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如何实现它。

2 个答案:

答案 0 :(得分:2)

Castle的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来覆盖返回值。您可以访问(并覆盖)参数。

答案 1 :(得分: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属性也有一个setter。