Loading...
Loading...
Marco Franssen

When using LINQ with your Entity Framework objects, you often need to remove duplicates from query results. For more advanced scenarios, you'll need an IEqualityComparer: for example, when comparing a specific property or several properties. However, writing a separate comparer for each case can leave you with a lot of repetitive infrastructure code.
You might end up with several equality comparer classes like these:
public class ProductIdEqualityComparer : IEqualityComparer<Product>
{
public bool Equals(Product x, Product y)
{
return x.Id == y.Id;
}
public int GetHashCode(Product obj)
{
return obj.Id.GetHashCode();
}
}public class ProductPriceEqualityComparer : IEqualityComparer<Product>
{
public bool Equals(Product x, Product y)
{
return x.Price == y.Price;
}
public int GetHashCode(Product obj)
{
return obj.Price.GetHashCode();
}
}public class PersonLastNameEqualityComparer : IEqualityComparer<Person>
{
public bool Equals(Person x, Person y)
{
return x.LastName == y.LastName;
}
public int GetHashCode(Person obj)
{
return obj.LastName.GetHashCode();
}
}There's a solution that saves you from writing all those classes. You only need two: one containing extension methods and the other a DelegateEqualityComparer.
public static class CompareExtensions
{
public static IEnumerable<T> Distinct<T>(this IEnumerable<T> items, Func<T, T, bool> equals, Func<T, int> hashCode)
{
return items.Distinct(new DelegateEqualityComparer<T>(equals, hashCode));
}
public static IEnumerable<T> Distinct<T>(this IEnumerable<T> items, Func<T, T, bool> equals)
{
return items.Distinct(new DelegateEqualityComparer<T>(equals, null));
}
}public class DelegateEqualityComparer<T> : IEqualityComparer<T>
{
private readonly Func<T, T, bool> _equals;
private readonly Func<T, int> _hashCode;
public DelegateEqualityComparer(Func<T, T, bool> equals, Func<T, int> hashCode)
{
_equals = equals;
_hashCode = hashCode;
}
public bool Equals(T x, T y)
{
return _equals(x, y);
}
public int GetHashCode(T obj)
{
if (_hashCode != null)
return _hashCode(obj);
return obj.GetHashCode();
}
}Now you can remove duplicates from your results by providing a lambda. I tried this with IQueryable, but it didn't work for my Entity Framework query. The LINQ provider generates SQL to execute the query, and we haven't supplied anything that translates our equality comparer into SQL. If you figure out how to make this work with IQueryable, please let me know.
_products.Distinct((x, y) => x.Id == y.Id, x => x.Id.GetHashCode());
_products.Distinct((x, y) => x.Price == y.Price, x => x.Price.GetHashCode());
_persons.Distinct((x, y) => x.LastName == y.LastName, x => x.LastName.GetHashCode());
_persons.Distinct((x, y) => x.FirstName == y.FirstName, x => x.FirstName.GetHashCode());
_persons.Distinct((x, y) => x.Address.City == y.Address.City, x => x.Address.City.GetHashCode());Share this article if you found it useful.
Marco Franssen
Use an old 256 MB USB stick to boot Windows PE, connect to a network share, and install Windows 8 without storing the full ISO on the USB drive.
Marco Franssen
Configure and test ASP.NET MVC 3 authorization with FluentSecurity, including role policies, Castle Windsor integration, and policy violation handlers.
Marco Franssen
Install Windows 8 Consumer Preview on a virtual hard disk alongside Windows 7, from creating a bootable USB drive to configuring the first boot.
Marco Franssen
Write a personal elevator pitch that highlights your strengths, builds credibility, and invites a conversation with practical tips on persuasion.