View Model Value Change Tracking in MvvmCross & Xamarin

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.

Share

Using HttpClient to Send Dates in URL Using AttributeRouting

After doing a lot of reading it appears that it is possible to do what I was attempting to do but it requires relaxing a lot of useful security measures in order to do so. Since there is a simple workaround it just doesn’t make sense to relax these measures in light of increased security risks.

The error I was getting at the API was:

A potentially dangerous Request.Path value was detected from the client (:)

Obviously this is the colon characters used to separate the elements of the time portion of the DateTime string. So I have made the following changes.

My Api action method now looks like this:

[System.Web.Http.HttpGet]
[GET("range?{startDate:datetime}&{endDate:datetime}")]
public HttpResponseMessage Get(DateTime startDate, DateTime endDate)

The dates are now defined as part of the query string rather than parts of the path itself.

To handle the creation of the query string I also have the following extension method:

public static string ToQueryString(this NameValueCollection source, bool removeEmptyEntries)
{
    return source != null ? "?" + String.Join("&", source.AllKeys
        .Where(key => !removeEmptyEntries || source.GetValues(key).Any(value => !String.IsNullOrEmpty(value)))
        .SelectMany(key => source.GetValues(key)
            .Where(value => !removeEmptyEntries || !String.IsNullOrEmpty(value))
            .Select(value => String.Format("{0}={1}", HttpUtility.UrlEncode(key), value != null ? HttpUtility.UrlEncode(value) : string.Empty)))
        .ToArray())
        : string.Empty;
}

Which is used in my client code like this:

var queryStringParams = new NameValueCollection
    {
        {"startDate", start.ToString(_dateService.DefaultDateFormatStringWithTime)},
        {"endDate", end.ToString(_dateService.DefaultDateFormatStringWithTime)}
    };

var response = httpClient.GetAsync(ApiRootUrl + "plots/range" + queryStringParams.ToQueryString(true)).Result;

The date service in my application simply provides the default date formatting string and uses this pattern:

“yyyy-MM-ddTHH:mm:ss”

The complete URI that is produced from this looks like:

http://localhost:51258/plots/range?startDate=2013-07-30T21%3A48%3A26&endDate=2013-08-06T21%3A48%3A26

Share

Serialization – Xml – Readonly Properties

I was recently working on a project where I needed to serialize objects with properties marked as readonly (can only be set inside a constructor).  The objects are part of a WCF RESTful service security infrastructure so I wasn’t about to change their design just because I needed to serialize them, there is no need too!  Anyway baring that in mind the lions share of the serialization duties in this project rely on POX (Plain Old Xml) serialisation and XmlFormatter’s etc.  So … if you have an object like this:

    [Serializable()]
    public class MyComplexObject
    {
        private string _thisIsPrivate = "This is the private string";
        private List<string> _thisIsAPrivateList = new List<string>();

        public readonly string Name;
        public readonly int Age;
        public readonly string HairColour;

        [NonSerialized()]
        public string WeDontWantThisSerialized;
        public string ThisIsntReadonly { get; set; }
        public int StringsInTheCollection
        {
            get { return _thisIsAPrivateList.Count; }
        }

        public MyComplexObject(string name, int age, string haircolour)
        {
            Name = name;
            Age = age;
            HairColour = haircolour;
        }

        public string GetThePrivateString()
        {
            return _thisIsPrivate;
        }

        public void AddAStringToTheCollection(string input)
        {
            _thisIsAPrivateList.Add(input);
        }
    }

You’re almost certainly going to run into Xml serialization issues unless you use a BinaryFormatter object and Serialize/Deserialize that way. In the next snippet you’ll see two extension methods written in C# that perform these tasks and extend both the Object and String types.

This first method takes your CLR object as input and produces a base 64 encoded string object from it ready for storing somewhere.

 public static string BinarySerializeToXmlString(this Object obj)
 {
     StringBuilder outputString = new StringBuilder();
     using (MemoryStream memoryStream = new MemoryStream())
     {
         using (XmlWriter writer = XmlWriter.Create(outputString))
         {
             BinaryFormatter bf = new BinaryFormatter();
             bf.Serialize(memoryStream, obj);
             memoryStream.Seek(0, SeekOrigin.Begin);
             writer.WriteElementString("ObjectDataElement", System.Convert.ToBase64String(memoryStream.ToArray()));
         }
     }
     return outputString.ToString();
 }

This next method takes in the string produced from the previous method and turns it back into a a CLR object of the type specified by T.

        public static T DeserializeFromBinaryXmlString<T>(this String input) where T : class
        {
            T deserialisedObject = null;
            byte[] data;
            using (XmlReader reader = XmlReader.Create(new StringReader(input)))
            {
                using (MemoryStream memoryStream = new MemoryStream())
                {
                    reader.Read();
                    reader.ReadStartElement();
                    data = System.Convert.FromBase64String(reader.ReadString());
                    memoryStream.Write(data, 0, data.Length);
                    memoryStream.Seek(0, SeekOrigin.Begin);
                    BinaryFormatter bf = new BinaryFormatter();
                    try
                    {
                        deserialisedObject = (T)bf.Deserialize(memoryStream);
                    }
                    catch (Exception ex)
                    {
                        // you'll probably want to log this instead!
                        if(Debugger.IsAttached) Console.WriteLine(ex.Message);
                    }
                }
            }
            return deserialisedObject;
        }

If you want to have a look at this in action I have produced a small demonstration solution which you can download and have a look at, pinch the code from etc

Share