如何动态实现代理模式?

发布于 2024-09-19 11:18:45 字数 1094 浏览 11 评论 0原文

我正在重新审视我去年写的班级跟踪(肮脏的逻辑)。目前,我有一个处理所有状态跟踪的 uber 基类,但是我需要跟踪其值的每个属性都需要遵循标准 get { return _x; } 设置 { _isDirty = true; _x = 值; } 工作方式。

在使用实体框架并阅读代理模式之后,我希望有一种更好的方法来实现我的 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 设置属性的值属性。

显然,我可以通过创建物理代理类并使用 IoC 轻松地做到这一点:

public class CustomerProxy : Customer
{
    Customer _customer;

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

    // Other properties
}

但我不喜欢对每个对象都这样做,否则我现有的解决方案没有任何好处。希望有人能满足我的好奇心,或者至少告诉我 EF 是如何实现的。

I'm revisiting my class tracking (dirty logic), which I wrote last year. Currently I have an uber base class that deals with all the state tracking, but each property whos values I need to track needs to stick to the standard get { return _x; } set { _isDirty = true; _x = value; } way of working.

After playing with Entity Framework and reading up on the Proxy Pattern, I was hoping there was a nicer way to implement my IsDIrty Logic whilst being able to make use of auto implemented properties?

To be completely honest, I haven't a clue of what I'm talking about. Is there a way I can do something like the following:

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; }

}

And then implement a dynamic proxy which will use reflection (or another magical solution) to call another method first before setting the values on the properties with the TrackState attribute.

Obviously I could easily do this by creating a phyiscal proxy class and use IoC:

public class CustomerProxy : Customer
{
    Customer _customer;

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

    // Other properties
}

But I don't fancy having to do this for every object, otherwise there's no benefit from my existing solution. Hope someone can satisfy my curiosity, or at least tell me how EF achieves it.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

时常饿 2024-09-26 11:18:45

Castle 的 DynamicProxy 正是这样做的: http://www.castleproject.org/dynamicproxy/index.html< /a>

允许您提供一个拦截器:

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

您可以通过 incalling.Method 访问 MethodInfo 对象。您可以通过设置incalling.ReturnValue来覆盖返回值。您可以访问(并覆盖)参数。

Castle's DynamicProxy does exactly this: http://www.castleproject.org/dynamicproxy/index.html

Allows you to provide an interceptor:

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

You get access to the MethodInfo object via invocation.Method. You may override the return value by setting invocation.ReturnValue. And you can access (and override) the arguments.

柒七 2024-09-26 11:18:45

PostSharp 可以提供帮助。

或者,如果您愿意,您可以为此编写自己的 IL 重写器。 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。

PostSharp could help.

Or if you're feeling like it, you can write your own IL-rewriter for this. Mono.Cecil is a great library that'll make it a breeze. Here is quick concoction:

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);
  }
}

It uses these helpers:

public class TrackStateAttribute : Attribute { }

public interface ITrackable { bool IsDirty { get; } }

Example code:

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

It's assumed that the IsDirty property will also have a setter.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文