How can I dynamically implement a proxy template?

I am revising my class tracking (dirty logic) that I wrote last year. Currently, I have a uber base class that deals with all state tracking, but for each property value that I need to track, I need to adhere to the standard get { return _x; } set { _isDirty = true; _x = value; }way of working.

After playing with the Entity Framework and reading the proxy template, I was hoping there was a better way to implement my IsDIrty logic while being able to use automatically implemented properties?

To be completely honest, I don't know what I'm talking about. Is there a way to 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; }

}

-, ( ) TrackState.

, , phyiscal proxy IoC:

public class CustomerProxy : Customer
{
    Customer _customer;

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

    // Other properties
}

, . , - , , , EF .

+3
2

DynamicProxy : http://www.castleproject.org/dynamicproxy/index.html

:

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

MethodInfo invocation.Method. , invocation.ReturnValue. ( ) .

+2

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 .

+2

Source: https://habr.com/ru/post/1764545/


All Articles