Question

At some point during my app, I am provided with a dictionary of data from a database query. I take that data and instance a object and invoke a RestoreState() method which takes the dictionary of data and applies the values to the corresponding properties within the new instance.

When the RestoreState() method is completed, i then pass the dictionary of values to the object into a property called OriginalValues like such:

myObject.EnableChangeTracking = true;
myObject.OrginalValues = dictionaryData;

Later on, the user makes some changes to the object via the UI and presses save. I then want to compare the existing state of the objects properties against the original state, so I can construct a update statement to send to SQL.

The problem (and question) is that my lambda expression to compare my current property value against the value in the dictionary does not work. The OriginalValues dictionary has 23 items, and the returned dictionary from my Lambda contains 14. In my unit test, I only change 2. Why am I getting back 12 additional values that have not changed?

My GetChanges method:

    public Dictionary<string, object> GetChanges()
    {
        if(!EnableChangeTracking || OrginalValues==null || !OrginalValues.Any())
            return new Dictionary<string, object>();

        // Only get properties that have changed.
        var changedProperties = 
            this
            .GetType()
            .GetProperties()
            .Where(p => p.GetValue(this, null) != OrginalValues[p.Name])
            .ToDictionary(p => p.Name, p => p.GetValue(this));

        return changedProperties;
    } 

My OriginalValues dictionary contains:

  • [0] {[AssetId, 1]} System.Collections.Generic.KeyValuePair
  • [1] {[SubFeatureId, 0]} System.Collections.Generic.KeyValuePair
  • [2] {[OrgNumber, 8555]} System.Collections.Generic.KeyValuePair
  • [3] {[LaneLocationId, ]} System.Collections.Generic.KeyValuePair
  • [4] {[StatusId, 1]} System.Collections.Generic.KeyValuePair
  • [5] {[AssetCode, 1]} System.Collections.Generic.KeyValuePair
  • [6] {[Note, There is nothing to see here.]} System.Collections.Generic.KeyValuePair
  • [7] {[DateActivated, 1/21/2014 9:12:08 AM]} System.Collections.Generic.KeyValuePair
  • [8] {[DateInactivated, 1/21/2014 9:12:08 AM]} System.Collections.Generic.KeyValuePair
  • [9] {[EnteredBy, JS]} System.Collections.Generic.KeyValuePair
  • [10] {[DateEntered, 1/21/2014 9:12:08 AM]} System.Collections.Generic.KeyValuePair
  • [11] {[CreatedBy, JS]} System.Collections.Generic.KeyValuePair
  • [12] {[UpdatedBy, JS]} System.Collections.Generic.KeyValuePair
  • [13] {[DateCreated, 1/21/2014 9:12:08 AM]} System.Collections.Generic.KeyValuePair
  • [14] {[DateUpdated, 1/21/2014 9:12:08 AM]} System.Collections.Generic.KeyValuePair
  • [15] {[IsActive, True]} System.Collections.Generic.KeyValuePair
  • [16] {[AssetAttributes, System.Collections.Generic.List`1[FISDC.Models.AssetAttribute]]} System.Collections.Generic.KeyValuePair
  • [17] {[AssetReasons, System.Collections.Generic.List`1[FISDC.Models.AssetReason]]} System.Collections.Generic.KeyValuePair
  • [18] {[AssetObjects, System.Collections.Generic.List`1[FISDC.Models.AssetObject]]} System.Collections.Generic.KeyValuePair
  • [19] {[SubFeature, ]} System.Collections.Generic.KeyValuePair
  • [20] {[EnableChangeTracking, False]} System.Collections.Generic.KeyValuePair
  • [21] {[IsValid, False]} System.Collections.Generic.KeyValuePair
  • [22] {[OrginalValues, ]} System.Collections.Generic.KeyValuePair

and my Lambda expression returns:

  • [0] {[AssetId, 1]} System.Collections.Generic.KeyValuePair
  • [1] {[SubFeatureId, 0]} System.Collections.Generic.KeyValuePair
  • [2] {[OrgNumber, 2222]} System.Collections.Generic.KeyValuePair
  • [3] {[StatusId, 1]} System.Collections.Generic.KeyValuePair
  • [4] {[DateActivated, 1/21/2014 9:12:08 AM]} System.Collections.Generic.KeyValuePair
  • [5] {[DateInactivated, 1/21/2014 9:12:08 AM]} System.Collections.Generic.KeyValuePair
  • [6] {[DateEntered, 1/21/2014 9:12:08 AM]} System.Collections.Generic.KeyValuePair
  • [7] {[CreatedBy, SomeoneElse]} System.Collections.Generic.KeyValuePair
  • [8] {[DateCreated, 1/21/2014 9:12:08 AM]} System.Collections.Generic.KeyValuePair
  • [9] {[DateUpdated, 1/21/2014 9:12:08 AM]} System.Collections.Generic.KeyValuePair
  • [10] {[IsActive, True]} System.Collections.Generic.KeyValuePair
  • [11] {[EnableChangeTracking, True]} System.Collections.Generic.KeyValuePair
  • [12] {[IsValid, False]} System.Collections.Generic.KeyValuePair
  • [13] {[OrginalValues, System.Collections.Generic.Dictionary`2[System.String,System.Object]]} System.Collections.Generic.KeyValuePair

The only properties i changed in my unit test were OrgNumber and CreatedBy. Anyone have any ideas?

Update

The following is the code I use to update the properties, then invoke the GetChanges() method.

        myObject.OrgNumber = "2222";
        myObject.CreatedBy = "SomeoneElse";

        myDictionary = myObject.GetChanges();

Update 2

I'm curious if the issue is that my dictionary is <string, object> and I am comparing against both reference and non-reference types? So a int value is compared against a object and returned? I don't know.

Update 3

Working method thanks to @TTat

    public Dictionary<string,object> OrginalValues { get; set; }

    public Dictionary<string, object> GetChanges()
    {
        if(!EnableChangeTracking || OrginalValues==null || !OrginalValues.Any())
            return new Dictionary<string, object>();

        // Only get properties that have changed.
        var changedProperties = 
            this
            .GetType()
            .GetProperties()
            .Where(p => !System.Attribute.IsDefined(p, typeof(IgnoreAttribute))
                         && !Equals(p.GetValue(this, null), OrginalValues[p.Name]))
            .ToDictionary(p => p.Name, p => p.GetValue(this));

        return changedProperties;
    } 
Was it helpful?

Solution

Since your dictionary is <string, object>, != will use a reference comparison. Use the Equals method to check the value. This uses the base Equals which will handle null results for you.

Where(p => !Equals(p.GetValue(this, null), OrginalValues[p.Name]))
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top