Wednesday, 10 August 2016

Generic method to compare values: less code to achieve more!

I recently had a requirement to verify whether a value has updated by comparing with the present value and the earlier value. In the context of Dynamics CRM, I needed to compare the value of an attribute in the target entity with the value of the same attribute from the preimage entity in a post update plugin on a custom entity.
I had to write a method which will compare the values of two object types and return true or false. However, the caveat is that these values are reference types (and I assumed they do not have any IComparer implementation and are sealed) and so the comparisons for each type will be different (need to use different properties). The obvious way was to write a method for each type, which would be difficult to maintain as there needs to be a method for each of the object types supported by the Dynamics CRM SDK. So I used an alternate approach where I wrote a single method (with the help of C# generics) that would cater to all the different object types.
Here goes the method:


private bool IsValueUpdated <T>(T presentValue, T oldValue, Func<T, T, bool> comparer) where T: class
        {
            if (presentValue == null)
            {
                // if the present value is null, it means it was not updated in the update plugin
                return false;
            }

            if (oldValue == null)
            {
                // if the old value is null, it means the old value was not populated but there is a new value (applicable during creation)
                return true;
            }

            return comparer(presentValue, oldValue);
        }


The above comments might be Dynamics CRM specific (but the idea is to help you understand the approach used and can be applicable to any .NET scenario as well).
The method is mostly easy to understand. However, I would like to point out the third parameter in the method the “comparer” Func. Funcs are a feature of C# that help us to encapsulate methods (a delegate type). We can pass our custom comparer method in the Func using a lambda expression as follows:

  var isTypeUpdated = this.IsValueUpdated(typePresentValue, typeOldValue,
                        (presentValue, oldValue) => presentValue.Value != oldValue.Value);


                    var isCompanyUpdated = this.IsValueUpdated(companyPresentValue, companyOldValue,
                        (presentValue, oldValue) => presentValue.Id != oldValue.Id);


The highlighted portion shows 2 different method invocations where we are passing the lambda expression for the comparer. If you note carefully, the types are automatically inferred by the C# compiler, so we do not need to explicitly state that the first invocation of the generic method is for the OptionsetValue type, while the second is for the EntityReference type (Dynamics CRM object types of interest for my case).
We can also create Func variables and store the lambda expressions as local variables which we can pass in case we require another method which has the same comparer implementation or we do not want to pass the lambdas during the method invocation.


Please let me know your views on the above approach and you are most welcome to share your ideas as well. Thanks in advance and have a great time with C#/Dynamics CRM!

No comments:

Post a Comment