Change tracking object contents at first might seem like an easy or straightforward task but there are lots of pitfalls and potential issues with many solutions. What works in your situation might not work in other situations due to implementation details.
The most obvious use of change tracking is at the heart of many ORM frameworks. These frameworks manage data, often through proxy classes, in order to track what needs to be written to the data store when inserts or updates are applied. These are often very granular and can be significant performance drains if misused or used in situations when not really required.
Most solutions you’ll find simply add an IsDirty property on a base class or interface and then just set that to true in any property setters in order to mark the class as dirty. Other solutions maybe a little smarter and have a SetProperty() method that compares the new value to the current value and only executes the set if the value is new or unequal. Whilst this isn’t change tracking it is at least efficient in view models when bound to interfaces. An example method might look like this:
protected void SetProperty<T>(ref T backingfield, T value, Expression<Func<T>> property)
{
if (Equals(backingfield, value))
{
return;
}
var mem = property.Body as MemberExpression;
if (mem != null)
{
var prop = mem.Member as PropertyInfo;
if (prop != null && prop.CanWrite)
{
// take advantage of any setter logic
prop.SetValue(this, value);
}
else
{
// update backing store
backingfield = value;
}
}
RaisePropertyChanged(property);
}
If you’re trying to compare to original values you could start tracking each change to each property and working out if the current value has been used at any point in past. This could very easily become a very bloated object graph. You’re also still left in a situation where developers have to remember to call the SetProperty() method and not just set a property either directly or miss the public class API altogether and set the private backing field and negating any setter logic anyway. If you don’t want to have property changed code in all your setters this can be a good approach if a little more expensive.
Negating all of these issues in one solution is extremely hard but minimising them is a must.
My Solution
I wanted a fairly simple IsDirty semantics at the class level. I wanted my solution to just take care of itself and I didn’t want to have to do too much work in order to get this reliably functioning. I also want it to be efficient and smart enough to accurately report that the class data really had changed and also wasn’t the same as the original values. My requirements where:
- Entirely PCL (Portable Class Library) based implementation
- Behaves in data binding situations
- Attribute-driven Opt-In
- Complex reference & value types included in object graph
- Public only tracking (this is a view model after all!)
- Filtering properties on Type or Name (string fuzzy matched)
- iOS, Android, WP & MvvmCross friendly
- As performance & memory efficient as possible
- Class level monitoring only
- Minimal extra library dependencies
So given these change tracking requirements I have coded my solution and I’m going to include all the code in a GitHub GIST below.
Usage
I wanted this to be as simple as possible pollute the code as little as possible. I think it’s OK at the moment but will no doubt change and hopefully get better.
Property Attribute Example
If you use the attribute only those properties will be monitored, all other properties will be ignored.
public class PersonViewModel : MvxViewModel
{
// ignored
public string Name { get; set; }
[IsDirtyMonitoring]
public string DisplayName { get; set; }
}
IsMonitoring Example
To start the monitoring, at some point after you can guarantee that all the values you regard as “clean” available in the object set Ismonitoring to true. This forces a the calculation of an MD5 value for all the properties, now any subsequent calls to the IsDirty getter will compare this hash value to a newly created one with the current data.
You can reset the clean hash at any point by simply setting IsMonitoring to true again.
public sync void Init(int id)
{
var data = await _myapiClient.GetPersonAsync(id);
Mapper.Map(data, this);
IsMonitoring = true;
}
Dependencies & PCL
The basic design has two dependencies. Since I already had Newtonsoft.Json used in my project I have made use of parts of this and in order to implement the MD5 object hashing I have had to add a library – the xBrainLab.Security.Cryptography library available on codeplex. Weighing in at 9,728 bytes this isn’t really much of a concern.
Basic Change Tracking Design
The basic design is that when IsMonitoring is set to true an object immediately generates an MD5 hash of all the relevant properties (default minus filters or explicitly opted in). Then each subsequent call to IsDirty will first generate a new MD5 hash of the current values and compares that to the stored “clean hash”.
Hash Values (MD5)
Hashing in PCLs isn’t the norm so we have to use a library. When generating the hash the object is first serialised to a memory stream using a JSON.NET serialiser. This is then MD5 hashed and either stored or compared in order to calculate if an object has changes since monitoring began.
You can also set a newly minted clean hash by simply setting IsMonitoring to true on live objects.
Class Serialisation
The serialisation is all handled my JSON.NET and it’s built in serialiser. I did try using the BSON serialiser as this may have a performance gain but it seemed to be erroring too often so I may have to revisit this. There is a custom contract resolver that controls what is or isn’t to be tracked. If a class has any properties marked with the IsDirtMonitoringAttribute then only these marked properties will be tracked. If none of the properties are marked with the attribute then all public properties will be included in the change tracking minus any properties of the same type as the filter types or property name masks in the name filter.
Performance
It’s not particularly scientific or probably that accurate but I have added a unit test that monitors it’s execution time. The test performs 100,000 IsDirty calls in a loop and will fail if it takes more than a second to complete based on the built in StopWatch class in System.Diagnostics it’s about as accurate as I’m needing. Considering this will be called pretty infrequently and usually on a background thread this performance doesn’t concern me. I have now implemented this on a few classes in an iOS application and I haven’t noticed any runtime issue (as yet!).
Anyway, enough wibbling as I should be out Christmas shopping … here’s the code … any comments or improvements really welcome.
Change tracking object contents at first might seem like an easy or straightforward task but there are lots of pitfalls and potential issues with many solutions. What works in your situation might not work in other situations due to implementation details.
The most obvious use of change tracking is at the heart of many ORM frameworks. These frameworks manage data, often through proxy classes, in order to track what needs to be written to the data store when inserts or updates are applied. These are often very granular and can be significant performance drains if misused or used in situations when not really required.
Most solutions you’ll find simply add an IsDirty property on a base class or interface and then just set that to true in any property setters in order to mark the class as dirty. Other solutions maybe a little smarter and have a SetProperty() method that compares the new value to the current value and only executes the set if the value is new or unequal. Whilst this isn’t change tracking it is at least efficient in view models when bound to interfaces. An example method might look like this:
If you’re trying to compare to original values you could start tracking each change to each property and working out if the current value has been used at any point in past. This could very easily become a very bloated object graph. You’re also still left in a situation where developers have to remember to call the SetProperty() method and not just set a property either directly or miss the public class API altogether and set the private backing field and negating any setter logic anyway. If you don’t want to have property changed code in all your setters this can be a good approach if a little more expensive.
Negating all of these issues in one solution is extremely hard but minimising them is a must.
My Solution
I wanted a fairly simple IsDirty semantics at the class level. I wanted my solution to just take care of itself and I didn’t want to have to do too much work in order to get this reliably functioning. I also want it to be efficient and smart enough to accurately report that the class data really had changed and also wasn’t the same as the original values. My requirements where:
So given these change tracking requirements I have coded my solution and I’m going to include all the code in a GitHub GIST below.
Usage
I wanted this to be as simple as possible pollute the code as little as possible. I think it’s OK at the moment but will no doubt change and hopefully get better.
Property Attribute Example
If you use the attribute only those properties will be monitored, all other properties will be ignored.
IsMonitoring Example
To start the monitoring, at some point after you can guarantee that all the values you regard as “clean” available in the object set Ismonitoring to true. This forces a the calculation of an MD5 value for all the properties, now any subsequent calls to the IsDirty getter will compare this hash value to a newly created one with the current data.
You can reset the clean hash at any point by simply setting IsMonitoring to true again.
Dependencies & PCL
The basic design has two dependencies. Since I already had Newtonsoft.Json used in my project I have made use of parts of this and in order to implement the MD5 object hashing I have had to add a library – the xBrainLab.Security.Cryptography library available on codeplex. Weighing in at 9,728 bytes this isn’t really much of a concern.
Basic Change Tracking Design
The basic design is that when IsMonitoring is set to true an object immediately generates an MD5 hash of all the relevant properties (default minus filters or explicitly opted in). Then each subsequent call to IsDirty will first generate a new MD5 hash of the current values and compares that to the stored “clean hash”.
Hash Values (MD5)
Hashing in PCLs isn’t the norm so we have to use a library. When generating the hash the object is first serialised to a memory stream using a JSON.NET serialiser. This is then MD5 hashed and either stored or compared in order to calculate if an object has changes since monitoring began.
You can also set a newly minted clean hash by simply setting IsMonitoring to true on live objects.
Class Serialisation
The serialisation is all handled my JSON.NET and it’s built in serialiser. I did try using the BSON serialiser as this may have a performance gain but it seemed to be erroring too often so I may have to revisit this. There is a custom contract resolver that controls what is or isn’t to be tracked. If a class has any properties marked with the IsDirtMonitoringAttribute then only these marked properties will be tracked. If none of the properties are marked with the attribute then all public properties will be included in the change tracking minus any properties of the same type as the filter types or property name masks in the name filter.
Performance
It’s not particularly scientific or probably that accurate but I have added a unit test that monitors it’s execution time. The test performs 100,000 IsDirty calls in a loop and will fail if it takes more than a second to complete based on the built in StopWatch class in System.Diagnostics it’s about as accurate as I’m needing. Considering this will be called pretty infrequently and usually on a background thread this performance doesn’t concern me. I have now implemented this on a few classes in an iOS application and I haven’t noticed any runtime issue (as yet!).
Anyway, enough wibbling as I should be out Christmas shopping … here’s the code … any comments or improvements really welcome.