3

昨年書いたクラス トラッキング (ダーティ ロジック) を再検討しています。現在、すべての状態追跡を処理する超基本クラスがありますが、追跡する必要がある値を持つ各プロパティは、標準的get { return _x; } set { _isDirty = true; _x = value; }な作業方法に固執する必要があります。

Entity Framework で遊んで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ます。

明らかに、物理プロキシ クラスを作成して IoC を使用することで、これを簡単に行うことができます。

public class CustomerProxy : Customer
{
    Customer _customer;

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

    // Other properties
}

しかし、すべてのオブジェクトに対してこれを行う必要はありません。そうしないと、既存のソリューションから何のメリットもありません。誰かが私の好奇心を満たしてくれることを願っています。または、少なくとも EF がそれをどのように達成しているか教えてください。

4

2 に答える 2

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プロパティにはセッターもあると想定されています。

于 2010-09-13T17:41:18.610 に答える
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 を設定することで、戻り値をオーバーライドできます。また、引数にアクセス (およびオーバーライド) できます。

于 2010-09-13T16:57:54.827 に答える