Wednesday 25 June 2014

A generic IEqualityComparer of T written in C#

When working with LINQ, often one has to pass in an IEqualityComparer of the class(es) being used. Implementing IEqualityComparer on classes can sometimes be unwanted to just make the computations work, therefore a generic implementation would be nice to invoke when performing the LINQ expression without either adding IEqualityComparer or worse - having to rewrite it. Sometimes also multiple implementations are desired.. The following class LambdaComparer is a generic implementation of IEqualityComparer of T.


using System;
using System.Collections.Generic;

namespace Hemit.OpPlan.Client.Infrastructure.Utility
{
    /// <summary>
    /// LambdaComparer - avoids the need for writing custom IEqualityComparers
    /// 
    /// Usage:
    /// 
    /// List<MyObject> x = myCollection.Except(otherCollection, new LambdaComparer<MyObject>((x, y) => x.Id == y.Id)).ToList();
    /// 
    /// or
    /// 
    /// IEqualityComparer comparer = new LambdaComparer<MyObject>((x, y) => x.Id == y.Id);
    /// List<MyObject> x = myCollection.Except(otherCollection, comparer).ToList();
    /// 
    /// </summary>
    /// <typeparam name="T">The type to compare</typeparam>
    public class LambdaComparer<T> : IEqualityComparer<T>
    {
        private readonly Func<T, T, bool> _lambdaComparer;
        private readonly Func<T, int> _lambdaHash;

        public LambdaComparer(Func<T, T, bool> lambdaComparer) :
            this(lambdaComparer, o => 0)
        {
        }

        public LambdaComparer(Func<T, T, bool> lambdaComparer, Func<T, int> lambdaHash)
        {
            if (lambdaComparer == null)
            {
                throw new ArgumentNullException("lambdaComparer");
            }

            if (lambdaHash == null)
            {
                throw new ArgumentNullException("lambdaHash");
            }

            _lambdaComparer = lambdaComparer;
            _lambdaHash = lambdaHash;
        }

        public bool Equals(T x, T y)
        {
            return _lambdaComparer(x, y);
        }

        public int GetHashCode(T obj)
        {
            return _lambdaHash(obj);
        }
    }
}


The following unit tests uses this implementation of a generic IEqualityComparer of T:

using System;
using System.Collections.Generic;
using System.Linq;
using Hemit.OpPlan.Client.Infrastructure.Utility;
using NUnit.Framework;

namespace Hemit.OpPlan.Client.Infrastructure.Test.Utility
{
    
    [TestFixture]
    public class LambdaComparerTest
    {

        [Test] 
        public void LambdaComparerPerformsExpected()
        {
            var countriesFirst = new List<Tuple<int, string>>{ 
                new Tuple<int, string>(1, "Spain"),
                new Tuple<int, string>(3, "Brazil"),
                new Tuple<int, string>(5, "Argentina"),
                new Tuple<int, string>(6, "Switzerland"),
                new Tuple<int, string>(7, "Uruguay"),
                new Tuple<int, string>(8, "Colombia")
            };
            var countriesSecond = new List<Tuple<int, string>>{
                new Tuple<int, string>(1, "Spain"),
                new Tuple<int, string>(4, "Portugal"),
                new Tuple<int, string>(7, "Uruguay"),
                new Tuple<int, string>(10, "England"),
                new Tuple<int, string>(11, "Belgium"),
                new Tuple<int, string>(12, "Greece")
            };

            var expected = new List<Tuple<int, string>>
            {            
                new Tuple<int, string>(3, "Brazil"),
                new Tuple<int, string>(5, "Argentina"),
                new Tuple<int, string>(6, "Switzerland"),               
                new Tuple<int, string>(8, "Colombia")                
            }; 

            var countriesOnlyInFirst = countriesFirst.Except(countriesSecond, new LambdaComparer<Tuple<int, string>>((x, y) => x.Item1 == y.Item1));

            CollectionAssert.AreEqual(countriesOnlyInFirst, expected); 

        }


    }


}


In the unit test above, two lists containing the topmost FIFA ranking country teams in soccer in the world are being used in a LINQ Except expression, where the generic LambdaComparer class is being used. The class being passed in is Tuple of int and string: Tuple<int,string> - Note that an ordinary class could also be used here. The property Item1 of the Tuple is the "key" that is being used to compare two different objects - if it is the same, the objects are being considered to be the same. This results into a list of items from the first country list that are not being present in the second list. Finally, a list of the expected result is being built up and the NUnit CollectionAssert.AreEqual method is used for checking consistent result. The test passes. Much of .NET requires implementing interfaces such as IEqualityComparer, IComparer and more. Using a generic implementation class that expects Func expressions (the lambda expressions being passed, is a pattern that can give much flexibility.

Wednesday 7 May 2014

Generic type conversion in C#

Generic type conversion in C# is possible, but the type conversion must consider many different conversions. The following code below defines a generic method To<T> which is an extension method on object:

namespace Hemit.OpPlan.Common
{
    
    public static class ObjectExtensions
    {

        public static T To<T>(this object value)
        {
            if (value == null)
                return default(T);

            Type t = typeof(T); 

            if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>))
            {
                Type valueType = t.GetGenericArguments()[0];
                if (value == null)
                    return default(T);
                object result = Convert.ChangeType(value, valueType);
                return (T)result;
            }
            else if (typeof(T).IsEnum)
            {
                object result = Enum.Parse(typeof(T), value.ToString());
                return (T)result;
            }
            else
            {
                try
                {
                    object result = Convert.ChangeType(value, typeof(T));
                    return (T)result;
                }
                catch (Exception err)
                {
                    Debug.WriteLine(err.Message);
                    return default(T); 
                }
            }
        }

    }
}

Next is a couple of unit tests written in NUnit for this extension method. All tests passes.

using System;
using System.Reflection;
using Hemit.OpPlan.Common.DataContract;
using NUnit.Framework;  

namespace Hemit.OpPlan.Common.Test.Extensions
{
    
    [TestFixture] 
    public class ObjectExtensionsTest
    {

        [Test]
        [TestCase("4", typeof(int), 4)]
        [TestCase("true", typeof(bool), true)]
        [TestCase(null, typeof(bool), false)]
        [TestCase(null, typeof(Nullable<bool>), null)]
        [TestCase("true", typeof(Nullable<bool>), true)]
        [TestCase("FutureOperation", typeof(OperationStatusDataContract), OperationStatusDataContract.FutureOperation)]
        [TestCase("Postponed", typeof(OperationStatusDataContract), OperationStatusDataContract.Postponed)]
        public void ToReturnsExpected(object input, Type genericTypeArgument, object expected)
        {
            MethodInfo method = typeof(ObjectExtensions).GetMethod("To");
            MethodInfo genericMethod = method.MakeGenericMethod(new Type[] { genericTypeArgument });
            object actual = genericMethod.Invoke(input, new object[]{ input });
            Assert.AreEqual(actual, expected); 
        }

        [Test] 
        public void ToReturnsExpectedWithNullableBoolean()
        {
            int? inputValue = new Nullable<int>(3);
            int actual = ((int?)inputValue).To<int>(); 
            Assert.AreEqual(actual, 3); 
        }

    }

}


The code above shows how one can call a generic method, shown in the unit test. Multiple test cases are passed into this unit test method. As is visible to the reader, the class ObjectExtensions contains the method To, which considers a conversion of an object to a designated type. The conversion itself must of course be valid, in addition one should check that the value implements IConvertible or similar interface. I have kept the code rather short and will eloborate the code if I see thare are conversions that fail, which should work.

Wednesday 23 April 2014

Using LinqKit and PredicateBuilder to create reusable predicates or filters in Entity Framework

If one has worked with Entity Framework for a while, sooner or later it becomes clear that refactoring logic into separate methods is hard, because Entity Framework must transform the code one writes in Linq to Entities into SQL. Therefore, extracting logic in EF into method calls will compile, but give a runtime error. The solution is to use the library LinqKit, created by brothers Joe and Ben Albahiri. It is available as a Nuget package: install-package LinqKit or at the following web site: LinqKit

The following simple example shows how it is possible to create a reusable predicate separated into a method of its own and call this method in Linq to entities code. Make note of the use of the AsExpandable() extension method that transforms the DbSet into an IQueryable by returning a wrapper that allows this. The code is run inside LinqPad. Make note that LinqPad has got inherent support for PredicateBuilder, but the LinqKit DLL was added as a reference. Also the namespace imports added where: System.Linq, System.Linq.Expressions and LinqKit. Press F4 to set up the additional references and imports in Linqpad.

public Expression<Func<Patient, bool>> InterestingPatient(params string[] keywords){
 var predicate = PredicateBuilder.False<Patient>(); 
 foreach (string keyword in keywords){
  string temp = keyword; 
  predicate = predicate.Or(p => p.Name.Contains(temp)); 
 }
 return predicate; 
}

void Main()
{
   Patients.AsExpandable().Where(InterestingPatient("Olsen", "Nilsen")).Dump(); 
}




The following images shows the output. Make note that Patient here is a table in the context of my project. The data shown here is test data (not real pasients of course):