Monday, September 19, 2016

Deep Object Comparison

As I mentioned in my last post you can't compare reference types using the == operator because it compares the memory addresses not the values. So even with everything being the same two objects will never equal each other.

Below is some sample code that actually does a deep comparison of object to figure out if they are similar or not.

By deep I mean it will recursively follow all enumerations down to their lowest levels and compare them as well.

You might notice that there is no comparison to check the objects are the same type.  If you are not familiar with T it is a generic and forces the compiler to ensure objects are of the same type to be passed into this method.

You'll also notice that values are compared as strings but for my purposes and at least to get you started this is sufficient.

If there was data you wanted to return you could also modify the return type to be a response object with a bool to indicate if objects are the same and a list of which fields differed per your needs.

      public bool ObjectsAreSame(T a, T b)
        {
            bool result = true;
            var t = a.GetType();
            var properties = t.GetProperties();

            foreach (var p in properties)
            {
                if (typeof(IEnumerable).IsAssignableFrom(p.GetType()) || typeof(IEnumerable<>).IsAssignableFrom(p.GetType()))
                {
                    ObjectsAreSame(a.GetType().GetProperty(p.Name).GetValue(a, null), b.GetType().GetProperty(p.Name).GetValue(b, null));
                }
                else
                {
                    if (a.GetType().GetProperty(p.Name).GetValue(a, null) != null && a.GetType().GetProperty(p.Name).GetValue(b, null) != null)
                    {
                        if ((a.GetType().GetProperty(p.Name).GetValue(a, null).ToString()) != (b.GetType().GetProperty(p.Name).GetValue(b, null).ToString()))
                        {
                            result = false;
                            break;
                        }
                    }
                }
            }

            return result;
        }

Thursday, September 15, 2016

C# Value vs Reference Types

A refresher for those who may have forgotten about the wild world of value versus reference type.

Assume you have an object as follows:

public class Person
{
    public string FirstName {get;set;}
    public string LastName {get;set;}
}

Now assume the following code is executed:

Person a = new Person() { FirstName = "John", LastName = "Doe"};
Person b = new Person() { FirstName = "John", LastName = "Doe"};

Asset.AreEqual(a,b);

This asset will fail.  At initial glance you might think "but the values are the same".  Yes, they are.  But that is not what is being evaluated!  Because a class is a reference type what is being asserted is that the memory location of a is the same as b.  In this case, they are not because they are two separate objects.

There are several ways to address this.  One way would be to override the Equals method on the Person object and do a field by field comparison.  Another way might be to override the ToString method which would return a value type that could be compared.

The way I prefer to do this is to create a method that uses reflection (and generics, recursion) thoroughly compare all the object's fields.  This way there is no refactoring if fields are added/removed/changed.

Value types, however, will pass this asset.  For example:

string a = "Hello!";
string b = "Hello!";

Asset.AreEqual(a.b);

In this case, we're not checking the reference but actually comparing the values.

Another interesting thing here that trips people up is if you pass an object by reference then all changes in a method will be reflected in the original object; not so in a value type.

Assume something like this:

public void ChangeName(Person aPerson)
{
    aPerson.FirstName = "Billy";
}

public void DoSomething()
{
    Person a = new Person() { FirstName = "John", LastName = "Doe"};

    ChangeName(a);

   Asset.AreEqual(a.FirstName, "Billy");
}

This will work because ChangeName received the memory reference to a and updated the name.  For a reference type, this will not work.

Tuesday, August 23, 2016

Entity Framework - Foreign Key Fields

Something to keep in mind when working with objects that require a foreign key value; Entity Framework can handle this automatically.

If you have an object that requires a foreign key value you might have found yourself calling SaveChanges to save one object to the database to get its ID so you could insert it into another object.

The better way is to use the ForeignKey property on your corresponding property.  The convention is [ForeignKey("ForeignTableNameHere")].  You'll also want to a a public property for the foreign table as well.

Now you can call SaveChanges once at the very end and the foreign key will get set across all the necessary objects.