/FluffySpoon.ChangeDetection

Allows you to compare what changes have been made between two values or objects.

Primary LanguageC#

Allows you to compare what changes have been made between two values or objects.

Install the package from NuGet

install-package FluffySpoon.ChangeDetection

Usage

In the examples below, the changes are of type Change which looks like this:

public struct Change
{
    /// <summary>
    /// The path of the property that changed.
    /// <example><see cref="string.Empty"/> for simple values.</example>
    /// <example>"MyComplexObject.MyComplexSubObject.MySimpleValue" when comparing two MyComplexObject instances.</example>
    /// </summary>
    public string PropertyPath
    {
        get;
    }

    /// <summary>
    /// The old value.
    /// </summary>
    public object OldValue
    {
        get; 
    }

    /// <summary>
    /// The new value.
    /// </summary>
    public object NewValue
    {
        get;
    }
}

Comparing simple values

ChangeDetector.GetChange("foo", "bar");

/* returns:
{
    PropertyPath: "",
    OldValue: "foo",
    NewValue: "bar"
}
*/

Comparing complex objects recursively

Consider the following complex class for the examples below:

class ComplexObject
{
    public string StringValue
    {
        get; set;
    }

    public int MyIntValue
    {
        get; set;
    }

    public ComplexObject SubObject
    {
        get; set;
    }
}

Example: Comparing object with sub-object

var a = new ComplexObject() {
    StringValue = "foo",
    MyIntValue = 28,
    SubObject = new ComplexObject() {
        StringValue = "bar",
        MyIntValue = 123
    }
};

var b = new ComplexObject() {
    StringValue = "foo",
    MyIntValue = 1337,
    SubObject = new ComplexObject() {
        StringValue = "baz",
        MyIntValue = 123
    }
};

ChangeDetector.GetChanges(a, b);

/* returns:
[
    {
        PropertyPath: "MyIntValue",
        OldValue: 28,
        NewValue: 1337
    },
    {
        PropertyPath: "SubObject.StringValue",
        OldValue: "bar",
        NewValue: "baz"
    }
]
*/

Example: Comparing specific complex object property (SubObject)

var a = new ComplexObject() {
    StringValue = "foo",
    MyIntValue = 28,
    SubObject = new ComplexObject() {
        StringValue = "bar",
        MyIntValue = 123
    }
};

var b = new ComplexObject() {
    StringValue = "foo",
    MyIntValue = 1337,
    SubObject = new ComplexObject() {
        StringValue = "baz",
        MyIntValue = 123
    }
};

ChangeDetector.GetChanges(a, b, x => x.SubObject);

/* returns:
[
    {
        PropertyPath: "StringValue",
        OldValue: "bar",
        NewValue: "baz"
    }
]
*/

Example: Comparing specific simple value on object (MyIntValue)

var a = new ComplexObject() {
    StringValue = "foo",
    MyIntValue = 28,
    SubObject = new ComplexObject() {
        StringValue = "bar",
        MyIntValue = 123
    }
};

var b = new ComplexObject() {
    StringValue = "foo",
    MyIntValue = 1337,
    SubObject = new ComplexObject() {
        StringValue = "baz",
        MyIntValue = 123
    }
};

ChangeDetector.GetChanges(a, b, x => x.MyIntValue);

/* returns:
[
    {
        PropertyPath: "MyIntValue",
        OldValue: 28,
        NewValue: 1337
    }
]
*/

Checking if a change is present

Instead of GetChange and GetChanges, you can use HasChanges to get a boolean indicating if any change has been detected or not.

Checking among existing changes

In addition to the HasChanges and HasChange methods, you can also check if a specific property has changed among existing changes.

var changes = ChangeDetector.GetChanges(a, b, x => x.MyIntValue);

changes.HasChangeFor(x => x.MyIntValue); //returns a bool
changes.HasChangeFor("MyIntValue"); //returns a bool

Circular references won't break the comparison

If circular references are detected, the given property that causes the circular dependency is not evaluated.