c# - How can I hook into all property setters of derived classes from a base class? -


i have .net web application intents , purposes of question crud many different domain objects.

a common theme across theses objects need know value properties have been modified child domain model properties. have 2 different systems in place this.

the value properties 1 trying sort out question.

right models inherit persistablemodel base has these fields , methods of note:

 private readonly list<string> _modifiedproperties = new list<string>();  public virtual modelstate state { get; set; }  public ienumerable<string> modifiedproperties { { return _modifiedproperties; } }  protected bool hasmodifiedproperties { { return 0 < _modifiedproperties.count; } }  public bool wasmodified(string propertyname)  {       return _modifiedproperties.contains(propertyname);  }  public void wasmodified(string propertyname, bool modified)  {      if (modified)      {          if (!wasmodified(propertyname)) _modifiedproperties.add(propertyname);      }      else       {          _modifiedproperties.remove(propertyname);      }  } 

then within each individual model whenever property set need call wasmodified string of property name , boolean value.

obviously tedious , error prone, want redesign base class automatically add entries dictionary when derived class's property set.

in research closest i've been able use postsharp out of question.

while working on different project came solution gets of way towards original goal.

note solution reliant upon dev express viewmodelbase base class, wouldn't hard make new base class features being used non dev express projects:

edit: found issue reset state logic, update removes issue.

 public abstract class updateablemodel : viewmodelbase {     private static readonly methodinfo getpropertymethod;     private static readonly methodinfo setpropertymethod;      private readonly bool _trackingenabled;     private readonly dictionary<string, tuple<expression, object>> _originalvalues;     private readonly list<string> _differingfields;      static updateablemodel()      {         getpropertymethod = typeof(updateablemodel).getmethod("getproperty", bindingflags.public | bindingflags.instance | bindingflags.nonpublic);         setpropertymethod = typeof(updateablemodel).getmethod("setproperty");     }      protected updateablemodel(bool isnewmodel)     {         _originalvalues = new dictionary<string, tuple<expression, object>>();         _differingfields = new list<string>();         if (isnewmodel) return;          state = modelstate.unmodified;         _trackingenabled = true;     }      public modelstate state     {         { return getproperty(() => state); }         set { base.setproperty(() => state, value); }     }      public new bool setproperty<t>(expression<func<t>> expression, t value)     {         var wasupdated = base.setproperty(expression, value);         if (_trackingenabled && wasupdated)         {             updatestate(expression, value);         }         return wasupdated;     }      /// <summary>     /// reset state meant called when discarding changes, reset state value unmodified , set modified values original value.     /// </summary>     public void resetstate()     {         if (!_trackingenabled) return;         foreach (var differingfield in _differingfields)         {             var type = getfunctype(_originalvalues[differingfield].item1);              var genericpropertysetter = setpropertymethod.makegenericmethod(type);             genericpropertysetter.invoke(this, new[]{_originalvalues[differingfield].item2});         }     }      /// <summary>     /// update state meant called after changes have been persisted, reset state value unmodified , update original values new values.     /// </summary>     public void updatestate()     {         if (!_trackingenabled) return;         foreach (var differingfield in _differingfields)         {             var type = getfunctype(_originalvalues[differingfield].item1);             var genericpropertysetter = getpropertymethod.makegenericmethod(type);             var value = genericpropertysetter.invoke(this, new object[] { _originalvalues[differingfield].item1 });              var newvalue = new tuple<expression, object>(_originalvalues[differingfield].item1,value);             _originalvalues[differingfield] = newvalue;         }          _differingfields.clear();         state = modelstate.unmodified;     }      private static type getfunctype(expression expr)     {         var lambda = expr lambdaexpression;         if (lambda == null)         {             return null;         }         var member = lambda.body memberexpression;         return member != null ? member.type : null;     }      private void updatestate<t>(expression<func<t>> expression, t value)     {         var propertyname = getpropertyname(expression);         if (!_originalvalues.containskey(propertyname))         {             _originalvalues.add(propertyname, new tuple<expression,object>(expression, value));         }          else         {             if (!compare(_originalvalues[propertyname].item2, value))             {                 _differingfields.add(propertyname);             }             else if (_differingfields.contains(propertyname))             {                 _differingfields.remove(propertyname);                                }              state = _differingfields.count == 0                  ? modelstate.unmodified                  : modelstate.modified;         }     }      private static bool compare<t>(t x, t y)     {         return equalitycomparer<t>.default.equals(x, y);     } 

another quick note, isnewmodel constructor flag serves differentiate objects create on ui level don't need kind of state tracking, , objects generated data access level need state tracking.


Popular posts from this blog