Using string constant for notify property changed - c#

I am working with some existing code and trying to figure out the advantage (if any) of using a string constant for the name of a property when implementing INotifyPropertyChanged interface.
So for example doing this:
/*
* Why use this instead of string literal
* in OnPropertyChanged below??
*/
public const string CustomerIdPropertyName = "CustomerId";
private int _customerId;
public int CustomerId
{
get
{
return _customerId;
}
set
{
if (_cusomterId != value)
{
_customerId = value;
OnPropertyChanged(CustomerIdPropertyName);
}
}
}
Instead of this:
private int _customerId;
public int CustomerId
{
get
{
return _customerId;
}
set
{
if (_cusomterId != value)
{
_customerId = value;
OnPropertyChanged("CustomerId");
}
}
}

Both versions are equally prone to typing errors.
If you have a somewhat recent version of .NET, your property changed handler should look like this:
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = this.PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
Then your property looks like this:
private int _customerId;
public int CustomerId
{
get
{
return _customerId;
}
set
{
if (_cusomterId != value)
{
_customerId = value;
this.OnPropertyChanged();
}
}
}
And you don't have any trouble with typing errors.

There isn't an advantage compiler wise, since both will end up being a constant value.
I can't imagine a real advantage in using the code like that way. Either ways it is easy to make a typo, and you are not going to reuse that constant for anything, so it is pointless.
I had love to see the new nameof keyword implement in the next version of .NET. Or even better, if possible, use [CallerMemberName] as Marc Gravell suggested.
The use of nameof will be useful when having custom calculated properties (like in WPF for example) that don't have their own getter / setter.

To answer your question (trying to figure out the advantage) : there is an advantage for an observer who know your type and wait for a specific property to change
void Observe(Customer c)
{
c.PropertyChanged += (s, e) =>
{
if (e.PropertyName == Customer.CustomerIdPropertyName)
{
MessageBox.Show("New id " + Customer.CustomerId);
}
}
}
If you want to go futher :
Typing errors can be avoided using a property selector expression to fill your CustomerIdPropertyName.
You won't need it with nameof keyword (CTP). If you don't have this kind of observer, CalleMemberNameAttribute is the easiest way.

I imagine it is just to avoid bugs caused by typos and try and make the code a little easier to read. Also if you change the name of the property it means changing the value of the const will then work for all code that is checking if the property has changed. e.g. imagine this code:
public void Main()
{
var obj = new ClassWithNotifier();
obj.OnPropertyChanged += ObjectPropertyChanged;
DoSomethingWithObj(obj);
}
private void ObjectPropertyChanged(string propertyName)
{
switch (propertyName) {
case ClassWithNotifier.CustomerIdPropertyName:
// If the constant changes this will still work
break;
case "SomeOtherPropertyName":
// If you change the property string that is passed here from
// your class ClassWithNotifier then this will now break
break;
}
}
In the example above, regardless of the value of the constant the code will work, and if you want to change the property name at some point then you only need to change the constant value and everything will still work with out having to find everywhere we are checking for the name (obviously if you want to change the name of the constant variable as well then you still need to find those references, but finding references to Public fields is easier than searching through the whole project for magic strings)

Related

call PropertyChange with public member, not string C#

I've looked around a bit online and wasn't able to easily find a solution to what i was looking for. It may be in part to me not being sure the terminology to search for.
I wanted to know is there a way to call the PropertyChanged event using the property member name like so...
private string height;
public string Height
{
get { return name; }
set
{
Set(ref height, value);
RaisePropertyChanged( ()=> Name);
}
}
rather than using the actual string which can be seen here..
private string height;
public string Height
{
get { return name; }
set
{
Set(ref height, value);
RaisePropertyChanged("Name");
}
}
Raise Event
protected void RaisePropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
I've attempted a solution to this, but wasn't able to come to a conclusion. I know there are methods out there somewhere and im sure there are more modern ways to do this. Its 2016!!
Since you appear to be using C# 6, you can use the nameof operator, which resolves the name of the referenced property at compile time, passing a string to your method.
RaisePropertyChanged(nameof(Name));
If you need to use an earlier C# compiler, you can resolve the name of the property through a lambda expression; this is what Entity Framework does for its Include operator. You can define a GetPropertyName utility method (see example), then call it like so:
RaisePropertyChanged(GetPropertyName(() => Name));

How to write MVVM properties?

With MVVM I always see these both methods for properties:
private int myProperty;
public int MyProperty
{
get { return myProperty; }
set
{
myProperty = value;
NotifyPropertyChanged("MyProperty");
}
}
and
private int myProperty;
public int MyProperty
{
get { return myProperty; }
set
{
myProperty = value;
NotifyPropertyChanged(m => m.MyProperty);
}
}
The first one use an hardcoded string to NotifyPropertyChanged and the second one use a lambda expression to NotifyPropertyChanged. I don't want to create a debate to ask what is the better solution but I would like to understand what are the differences bewteeen these two solution. What are the consequences of using one or the other?
Correct me if I'm wrong but the lambda expression solution should use more memory and should be slower than the hardcoded string because the NotifyPropertyChanged method of the base class use delegate and reflection. But the hardcoded string solution may create stupid bug because it's a string and nothing to tell me I correctly wrote it.
The second expression would either generate a compiler error on the property name change, or would automatically change with it (via the Rename support in VS or ReSharper).
So basically, you gain compiler support for property names. The wrong name provided to the notify would mean that data-binding would break. With string names, this breakage would be silent.
In a small UI the choice is irrelevant in my opinion, but in a large application with a heavy UI layer the extra support against bugs can pay itself off in the long run.
Performance won't be problematically slower, don't forget, binding is reflection-powered anyway. Performance, as always, is relative. The hard-coded version will technically be faster because it doesn't need to reflect the property name out of the meta-data. How much faster, I'm not sure.
Rather than repeating that code of NotifyPropertyChanged for every property, I felt the below code is more cleaner
Create a Set method in your ViewModel Base
protected bool Set<T>(Expression<Func<T>> selectorExpression, ref T field, T value)
{
if (EqualityComparer<T>.Default.Equals(field, value))
return false;
field = value;
RaisePropertyChanged(selectorExpression);
return true;
}
and use them as
string title;
public string Title
{
get { return title; }
set { Set(() => Title, ref title, value); }
}
I use the following method in a base class implementing INotifyPropertyChanged and it is so easy and convenient:
public void NotifyPropertyChanged()
{
StackTrace stackTrace = new StackTrace();
MethodBase method = stackTrace.GetFrame(1).GetMethod();
if (!(method.Name.StartsWith("get_") || method.Name.StartsWith("set_")))
{
throw new InvalidOperationException("The NotifyPropertyChanged() method can only be used from inside a property");
}
string propertyName = method.Name.Substring(4);
RaisePropertyChanged(propertyName);
}

.NET user settings event handlers

I am trying to use the built in .NET application settings. So for instance I have a User setting of year.
If the end user changes the setting in the program I need to respond by refreshing the data displayed.
I currently have code like below to do this:
Settings.Default.PropertyChanged += SettingsChanged;
//on year change clear out the grid and update the data
private void SettingsChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName == "year")
{
grdStudentCourseSearch.DataSource = null;
grdStudentCourseSearch.DataMember = null;
UpdateData();
}
}
As you can see their seems to only be an event handler for all the settings and I am having to use e.PropertyName to compare a string to see which property has changed. Is there a better way to do this? Potentially if I change property names later this could be forgotten.
I believe there isn't a better way (using the generated Settings class), due to the implementation of the Settings Class
Consider the generated class code for a simple string setting:
public string Test {
get {
return ((string)(this["Test"]));
}
set {
this["Test"] = value;
}
}
As you can see, it uses the indexer with a string value - you don't have any specialized TestChanged event or some such. The call to OnPropertyChanged is in the indexer setter:
set
{
SettingChangingEventArgs e = new SettingChangingEventArgs(propertyName, base.GetType().FullName, this.SettingsKey, value, false);
this.OnSettingChanging(this, e);
if (!e.Cancel)
{
base[propertyName] = value;
PropertyChangedEventArgs args2 = new PropertyChangedEventArgs(propertyName);
this.OnPropertyChanged(this, args2);
}
}
You could choose to implement settings like this:
class Settings
{
public event EventHandler YearChanged;
private int _year;
public int Year
{
get { return _year; }
set
{
if (_year != value)
{
_year = value;
OnYearChanged(EventArgs.Empty);
}
}
}
protected virtual void OnYearChanged(EventArgs e)
{
if (YearChanged != null)
YearChanged(this, e);
}
}
Then you could register on the YearChanged event.
No, this isn't a good idea. The code is much too brittle. Catch this earlier. At the user interface level for example, whatever code you have that sets the setting value. It can fire the event and you'll know exactly what is getting modified. Or make an intermediate helper class.
In earlier .net framework (3.5 i think) we can use nameof keyword to avoid magic strings so e.PropertyChange == nameof(Year). So you will be warned by compiler when the property identifier changed.
You could just create a variable and assign the current setting to it at runtime, then just update the one variable whenever it's changed, after comparing it to the previous.

Is there a good strongly typed way to do PropertyChanged events in C#?

It must be a somewhat common event to change the name of a property and expect the Rename functionality in Visual Studio to take care of all the necessary renaming, except for the property name of the PropertyChanged event of INotifyPropertyChanged. Is there a better way to somehow get it strongly typed so you don't need to remember to manually rename it?
Edit: nameof arrived in c# 6. Yay!
There is no nameof / infoof etc; this is much discussed, but it is what it is.
There is a way to do it using lambda expressions in .NET 3.5 (and parsing the expression tree), but in reality it isn't worth the overhead. For now, I'd just stick with strings (and unit tests if you are determined not to break it).
using System;
using System.ComponentModel;
using System.Linq.Expressions;
using System.Reflection;
class Program : INotifyPropertyChanged {
public event PropertyChangedEventHandler PropertyChanged;
static void Main() {
var p = new Program();
p.PropertyChanged += (s, a) => Console.WriteLine(a.PropertyName);
p.Name = "abc";
}
protected void OnPropertyChanged<T>(Expression<Func<Program, T>> property) {
MemberExpression me = property.Body as MemberExpression;
if (me == null || me.Expression != property.Parameters[0]
|| me.Member.MemberType != MemberTypes.Property) {
throw new InvalidOperationException(
"Now tell me about the property");
}
var handler = PropertyChanged;
if (handler != null) handler(this,
new PropertyChangedEventArgs(me.Member.Name));
}
string name;
public string Name {
get{return name;}
set {
name = value;
OnPropertyChanged(p=>p.Name);
}
}
}
C# 5 seem to have a solution. With an CallerMemberName attribute that can be used with parameters (One example on the net).
class Employee : INotifyPropertyChanged
{
private string _Name;
public string Name
{
get { return _Name; }
set
{
_Name = value;
RaisePropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged([CallerMemberName] string caller = "")
{
var temp = PropertyChanged;
if ( temp != null )
{
temp( this, new PropertyChangedEventArgs( caller ) );
}
}
}
The simplest solution is to look at the stack trace and completly remove every explicit reference to the property.
public String Name
{
get { return this.name; }
set
{
if (value != this.name)
{
this.RaisePropertyChanging();
this.name = value;
this.RaisePropertyChanged();
}
}
}
private String name = null;
private void RaisePropertyChanged()
{
String propertyName =
new StackTrace().GetFrame(1).GetMethod().Name.SubString(4);
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
handler(new PropertyChangedEventArgs(propertyName));
}
}
The code derives the property name through the stack trace from the caling method - that is the property setter method named set_<PropertyName>. If the compiler no longer follows this naming convention, the code breaks.
The other solution is to derive the property name from a lambda expression.
public static String GetPropertyNameFromLambdaExpression<TObject, TProperty>(
Expression<Func<TObject, TProperty>> expression)
{
return ((MemberExpression)expression.Body).Member.Name;
}
For example
GetPropertyNameFromLambdaExpression<String, Int32>(s => s.Length)
will return "Length" as exspected. A production version of the code really demands additional checks and better integration into the rest of the code. For example it is possible to use type inference for the generic arguments.
UPDATE
And there is a third solution - you can use MethodBase.GetCurrentMethod() inside a property getter or setter to obtain the name of the setter or getter method.
public String Name
{
get { return this.name; }
set
{
if (value != this.name)
{
String propertyName = MethodBase.GetCurentMethod().Name.SubString(4);
this.RaisePropertyChanging(propertyName);
this.name = value;
this.RaisePropertyChanged(propertyName);
}
}
}
private String name = null;
In theory, you could use MethodBase.GetCurrentMethod().Name.Substring(4) from within the property setter. Unfortunately, Google search reveals that it seems to have a significant performance impact. Two more things to consider:
JIT inlining can impact this in unexpected ways. (stackoverflow.com/questions/616779/can-i-check-if-the-c-compiler-inlined-a-method-call)
In theory, the IL call to MethodBase.GetCurrentMethod() could be trivially replaced by the JIT at runtime with a ldtoken instruction followed by a call to MethodBase.GetMethodFromHandle(), which would be very fast. I guess users just haven't expressed a need for this. (msdn.microsoft.com/en-us/library/system.reflection.emit.opcodes.ldtoken.aspx)
Completely my opinion here, but I think it'd be nice to have fieldof() and methodof() operators in C#. I believe it would greatly improve the reliability of the code analysis/refactoring tools in projects that require that ability.
Not an answer to your question, but if you right-click->Refactor->Rename a property, it can rename matching strings as well, including any strings that match your property's name.
Yeah, it can be a bit dangerous.
You should check out this blog post. It gives you the ability to do this:
string propertyName = TypeHelper.GetPropertyName<User>(u => u.LastProjectCode);
PropertyInfo property1 = TypeHelper.GetProperty((SomeClass o) => o.InstanceProperty.Length);
PropertyInfo property2 = TypeHelper.GetProperty(() => SomeClass.StaticProperty.Length);
Renames in Visual Studio/Resharper/Refactor Pro should work for you then.
The PropertyChangedEventArgs only takes one constructor, which requires the property name as a string. So essentially no- utilizing INotifyPropertyChanged means that at some level, be it high or low in your architecture, you will have to work with a string and manual renaming.

c# marking class property as dirty

The following is a simple example of an enum which defines the state of an object and a class which shows the implementation of this enum.
public enum StatusEnum
{
Clean = 0,
Dirty = 1,
New = 2,
Deleted = 3,
Purged = 4
}
public class Example_Class
{
private StatusEnum _Status = StatusEnum.New;
private long _ID;
private string _Name;
public StatusEnum Status
{
get { return _Status; }
set { _Status = value; }
}
public long ID
{
get { return _ID; }
set { _ID = value; }
}
public string Name
{
get { return _Name; }
set { _Name = value; }
}
}
when populating the class object with data from the database, we set the enum value to "clean". with the goal of keeping most of the logic out of the presentation layer, how can we set the enum value to "dirty" when a property is changed.
i was thinking something along the lines of;
public string Name
{
get { return _Name; }
set
{
if (value != _Name)
{
_Name = value;
_Status = StatusEnum.Dirty;
}
}
}
in the setter of each property of the class.
does this sound like a good idea, does anyone have any better ideas on how the dirty flag can be assigned without doing so in the presentation layer.
When you really do want a dirty flag at the class level (or, for that matter, notifications) - you can use tricks like below to minimise the clutter in your properties (here showing both IsDirty and PropertyChanged, just for fun).
Obviously it is a trivial matter to use the enum approach (the only reason I didn't was to keep the example simple):
class SomeType : INotifyPropertyChanged {
private int foo;
public int Foo {
get { return foo; }
set { SetField(ref foo, value, "Foo"); }
}
private string bar;
public string Bar {
get { return bar; }
set { SetField(ref bar, value, "Bar"); }
}
public bool IsDirty { get; private set; }
public event PropertyChangedEventHandler PropertyChanged;
protected void SetField<T>(ref T field, T value, string propertyName) {
if (!EqualityComparer<T>.Default.Equals(field, value)) {
field = value;
IsDirty = true;
OnPropertyChanged(propertyName);
}
}
protected virtual void OnPropertyChanged(string propertyName) {
var handler = PropertyChanged;
if (handler != null) {
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
You might also choose to push some of that into an abstract base class, but that is a separate discussion
One option is to change it on write; another is to keep a copy of all the original values and compute the dirtiness when anyone asks for it. That has the added benefit that you can tell exactly which fields have changed (and in what way) which means you can issue minimal update statements and make merge conflict resolution slightly easier.
You also get to put all the dirtiness-checking in one place, so it doesn't pollute the rest of your code.
I'm not saying it's perfect, but it's an option worth considering.
If you want to implement it in this way, and you want to reduce the amount of code, you might consider applying Aspect Oriented Programming.
You can for instance use a compile-time weaver like PostSharp , and create an 'aspect' that can be applied to properties. This aspect then makes sure that your dirty flag is set when appropriate.
The aspect can look like this:
[Serializable]
[AttributeUsage(AttributeTargets.Property)]
public class ChangeTrackingAttribute : OnMethodInvocationAspect
{
public override void OnInvocation( MethodInvocationEventArgs e )
{
if( e.Delegate.Method.ReturnParameter.ParameterType == typeof(void) )
{
// we're in the setter
IChangeTrackable target = e.Delegate.Target as IChangeTrackable;
// Implement some logic to retrieve the current value of
// the property
if( currentValue != e.GetArgumentArray()[0] )
{
target.Status = Status.Dirty;
}
base.OnInvocation (e);
}
}
}
Offcourse, this means that the classes for which you want to implement ChangeTracking, should implement the IChangeTrackable interface (custom interface), which has at least the 'Status' property.
You can also create a custom attribute ChangeTrackingProperty, and make sure that the aspect that has been created above, is only applied to properties that are decorated with this ChangeTrackingProperty attribute.
For instance:
public class Customer : IChangeTrackable
{
public DirtyState Status
{
get; set;
}
[ChangeTrackingProperty]
public string Name
{ get; set; }
}
This is a little bit how I see it.
You can even make sure that PostSharp checks at compile-time whether classes that have properties that are decorated with the ChangeTrackingProperty attribute, implement the IChangeTrackable interface.
This method is based on a set of different concepts provided in this thread. I thought i'd put it out there for anyone that is looking for a way to do this cleanly and efficiently, as i was myself.
The key of this hybrid concept is that:
You don't want to duplicate the data to avoid bloating and resource hogging;
You want to know when the object's properties have changed from a given original/clean state;
You want to have the IsDirty flag be both accurate, and require little processing time/power to return the value; and
You want to be able to tell the object when to consider itself clean again. This is especially useful when building/working within the UI.
Given those requirements, this is what i came up with, and it seems to be working perfectly for me, and has become very useful when working against UIs and capturing user changes accurately. I have also posted an "How to use" below to show you how I use this in the UI.
The Object
public class MySmartObject
{
public string Name { get; set; }
public int Number { get; set; }
private int clean_hashcode { get; set; }
public bool IsDirty { get { return !(this.clean_hashcode == this.GetHashCode()); } }
public MySmartObject()
{
this.Name = "";
this.Number = -1;
MakeMeClean();
}
public MySmartObject(string name, int number)
{
this.Name = name;
this.Number = number;
MakeMeClean();
}
public void MakeMeClean()
{
this.clean_hashcode = this.Name.GetHashCode() ^ this.Number.GetHashCode();
}
public override int GetHashCode()
{
return this.Name.GetHashCode() ^ this.Number.GetHashCode();
}
}
It's simple enough and addresses all of our requirements:
The data is NOT duplicated for the dirty check...
This takes into account all property changes scenarios (see scenarios below)...
When you call the IsDirty property, a very simple and small Equals operation is performed and it is fully customizable via the GetHashCode override...
By calling the MakeMeClean method, you now have a clean object again!
Of course you can adapt this to encompass a bunch of different states... it's really up to you. This example only shows how to have a proper IsDirty flag operation.
Scenarios
Let's go over some scenarios for this and see what comes back:
Scenario 1
New object is created using empty constructor,
Property Name changes from "" to "James",
call to IsDirty returns True! Accurate.
Scenario 2
New object is created using paramters of "John" and 12345,
Property Name changes from "John" to "James",
Property Name changes back from "James" to "John",
Call to IsDirty returns False. Accurate, and we didn't have to duplicate the data to do it either!
How to use, a WinForms UI example
This is only an example, you can use this in many different ways from a UI.
Let's say you have a two forms ([A] and [B]).
The first([A]) is your main form, and the second([B]) is a form that allows the user to change the values within the MySmartObject.
Both the [A] and the [B] form have the following property declared:
public MySmartObject UserKey { get; set; }
When the user clicks a button on the [A] form, an instance of the [B] form is created, its property is set and it is displayed as a dialog.
After form [B] returns, the [A] form updates its property based on the [B] form's IsDirty check. Like this:
private void btn_Expand_Click(object sender, EventArgs e)
{
SmartForm form = new SmartForm();
form.UserKey = this.UserKey;
if(form.ShowDialog() == DialogResult.OK && form.UserKey.IsDirty)
{
this.UserKey = form.UserKey;
//now that we have saved the "new" version, mark it as clean!
this.UserKey.MakeMeClean();
}
}
Also, in [B], when it is closing, you can check and prompt the user if they are closing the form with unsaved changes in it, like so:
private void BForm_FormClosing(object sender, FormClosingEventArgs e)
{
//If the user is closing the form via another means than the OK button, or the Cancel button (e.g.: Top-Right-X, Alt+F4, etc).
if (this.DialogResult != DialogResult.OK && this.DialogResult != DialogResult.Ignore)
{
//check if dirty first...
if (this.UserKey.IsDirty)
{
if (MessageBox.Show("You have unsaved changes. Close and lose changes?", "Unsaved Changes", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.No)
e.Cancel = true;
}
}
}
As you can see from the examples above, this can be a very useful thing to have since it really streamlines the UI.
Caveats
Every time you implement this, you have to customize it to the object you're using. E.g.: there's no "easy" generic way of doing this without using reflection... and if you use reflection, you lose efficiency, especially in large and complex objects.
Hopefully this helps someone.
Take a look at PostSharp (http://www.postsharp.org/).
You can easily create a Attribute which marks it as dirty you can add the attrubute to each property that needs it and it keeps all your code in one place.
Roughly speaking Create an interface which has your status in make the class implement it.
Create an attribute which can be applied on properties and cast to your interface in order to set the value when something changes one of the marked properties.
Your approach is basically how I would do it. I would just
remove the setter for the Status property:
public StatusEnum Status
{
get { return _Status; }
// set { _Status = value; }
}
and instead add a function
public SetStatusClean()
{
_Status = StatusEnum.Clean;
}
As well as SetStatusDeleted() and SetStatusPurged(), because I find it better indicates the intention.
Edit
Having read the answer by Jon Skeet, I need to reconsider my approach ;-) For simple objects I would stick with my way, but if it gets more complex, his proposal would lead to much better organised code.
If your Example_Class is lightweight, consider storing the original state and then comparing the current state to the original in order to determine the changes. If not your approach is the best because stroing the original state consumes a lot of system resources in this case.
Apart from the advice of 'consider making your type immutable', here's something I wrote up (and got Jon and Marc to teach me something along the way)
public class Example_Class
{ // snip
// all properties are public get and private set
private Dictionary<string, Delegate> m_PropertySetterMap;
public Example_Class()
{
m_PropertySetterMap = new Dictionary<string, Delegate>();
InitializeSettableProperties();
}
public Example_Class(long id, string name):this()
{ this.ID = id; this.Name = name; }
private void InitializeSettableProperties()
{
AddToPropertyMap<long>("ID", value => { this.ID = value; });
AddToPropertyMap<string>("Name", value => { this.Name = value; });
}
// jump thru a hoop because it won't let me cast an anonymous method to an Action<T>/Delegate
private void AddToPropertyMap<T>(string sPropertyName, Action<T> setterAction)
{ m_PropertySetterMap.Add(sPropertyName, setterAction); }
public void SetProperty<T>(string propertyName, T value)
{
(m_PropertySetterMap[propertyName] as Action<T>).Invoke(value);
this.Status = StatusEnum.Dirty;
}
}
You get the idea.. possible improvements: Use constants for PropertyNames & check if property has really changed.
One drawback here is that
obj.SetProperty("ID", 700); // will blow up int instead of long
obj.SetProperty<long>("ID", 700); // be explicit or use 700L
Here is how i do it.
In cases where i do not need to test for specific fields being dirty,
I have an abstract class:
public abstract class SmartWrap : ISmartWrap
{
private int orig_hashcode { get; set; }
private bool _isInterimDirty;
public bool IsDirty
{
get { return !(this.orig_hashcode == this.GetClassHashCode()); }
set
{
if (value)
this.orig_hashcode = this.orig_hashcode ^ 108.GetHashCode();
else
MakeClean();
}
}
public void MakeClean()
{
this.orig_hashcode = GetClassHashCode();
this._isInterimDirty = false;
}
// must be overridden to return combined hashcodes of fields testing for
// example Field1.GetHashCode() ^ Field2.GetHashCode()
protected abstract int GetClassHashCode();
public bool IsInterimDirty
{
get { return _isInterimDirty; }
}
public void SetIterimDirtyState()
{
_isInterimDirty = this.IsDirty;
}
public void MakeCleanIfInterimClean()
{
if (!IsInterimDirty)
MakeClean();
}
/// <summary>
/// Must be overridden with whatever valid tests are needed to make sure required field values are present.
/// </summary>
public abstract bool IsValid { get; }
}
}
As well as an interface
public interface ISmartWrap
{
bool IsDirty { get; set; }
void MakeClean();
bool IsInterimDirty { get; }
void SetIterimDirtyState();
void MakeCleanIfInterimClean();
}
This allows me to do partial saves, and preserve the IsDirty state if there is other details to save. Not perfect, but covers a lot of ground.
Example of usage with interim IsDirty State (Error wrapping and validation removed for clarity):
area.SetIterimDirtyState();
if (!UpdateClaimAndStatus(area))
return false;
area.MakeCleanIfInterimClean();
return true;
This is good for most scenarios, however for some classes i want to test for each field with a backing field of original data, and either return a list of changes or at least an enum of fields changed.
With an enum of fields changed i can then push that up through a message chain for selective update of fields in remote caches.
You could also think about boxing your variables, which comes at a performance cost, but also has its merits. It is pretty consise and you cannot accidentally change a value without setting your dirty status.
public class Variable<T>
{
private T _value;
private readonly Action<T> _onValueChangedCallback;
public Variable(Action<T> onValueChangedCallback, T value = default)
{
_value = value;
_onValueChangedCallback = onValueChangedCallback;
}
public void SetValue(T value)
{
if (!EqualityComparer<T>.Default.Equals(_value, value))
{
_value = value;
_onValueChangedCallback?.Invoke(value);
}
}
public T GetValue()
{
return _value;
}
public static implicit operator T(Variable<T> variable)
{
return variable.GetValue();
}
}
and then hook in a callback that marks your class as dirty.
public class Example_Class
{
private StatusEnum _Status = StatusEnum.New;
private Variable<long> _ID;
private Variable<string> _Name;
public StatusEnum Status
{
get { return _Status; }
set { _Status = value; }
}
public long ID => _ID;
public string Name => _Name;
public Example_Class()
{
_ID = new Variable<long>(l => Status = StatusEnum.Dirty);
_Name = new Variable<string>(s => Status = StatusEnum.Dirty);
}
}
Another method is to override the GetHashCode() method to somthing like this:
public override int GetHashCode() // or call it GetChangeHash or somthing if you dont want to override the GetHashCode function...
{
var sb = new System.Text.StringBuilder();
sb.Append(_dateOfBirth);
sb.Append(_marital);
sb.Append(_gender);
sb.Append(_notes);
sb.Append(_firstName);
sb.Append(_lastName);
return sb.ToString.GetHashCode();
}
Once loaded from the database, get the hash code of the object. Then just before you save check if the current hash code is equal to the previous hash code. if they are the same, don't save.
Edit:
As people have pointed out this causes the hash code to change - as i use Guids to identify my objects, i don't mind if the hashcode changes.
Edit2:
Since people are adverse to changing the hash code, instead of overriding the GetHashCode method, just call the method something else. The point is detecting a change not whether i use guids or hashcodes for object identification.

Categories