Blog.

Delegate your equality comparisons

Marco Franssen

Marco Franssen /

3 min read429 words

Cover Image for Delegate your equality comparisons

When using Linq on your Entity Framework objects, you often need to distinct your query results. Therefore you need to implement an IEqualityComparer for the more advance scenario's. For example if you want to distinct on a specific property, or maybe on multiple properties. However this forces you to write lots of infrastructure code to distinct each type.

You probably would end up with several equality compare classes like this.

ProductIdEqualityComparer.cs
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();
    }
}
ProductPriceEqualityComparer.cs
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();
    }
}
PersonLastNameEqualityComparer.cs
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();
    }
}

However there is a solution which will save you the work to write all these classes. You will have to write only two classes. One will contain some extension methods, the other is a DelegateEqualityComparer.

CompareExtensions.cs
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));
    }
}
DelegateEqualityComparer.cs
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 simply distinct your query by providing a lambda. I tried it on IQueryable, but this doesn't work. Linq will generate some SQL to do the actual query. We didn't specified any code that can translate the equality comparer to SQL. If someone figures out how to make it 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.

You have disabled cookies. To leave me a comment please allow cookies at functionality level.

More Stories

Cover Image for Install Windows 8 from rusty 256 MB USB stick

Install Windows 8 from rusty 256 MB USB stick

Marco Franssen

Marco Franssen /

This is the fourth time I installed Windows 8. This time I installed it on my personal notebook instead of a VHD, because Windows 8 is finally ready to market. So I started with downloading the enterprise edition from my MSDN subscription. Unfortunately my USB drive died so I had no storage large enough to put the image on and boot from. So I started thinking to install it over the network. Luckily me I still had my rusty 10 year old 256MB USB drive which perfectly fits a Windows PE image. So I…

Cover Image for Secure your web app fluently

Secure your web app fluently

Marco Franssen

Marco Franssen /

When building a big web application with ASP.NET MVC 3 I ran into a problem to secure my web application in a maintainable way. There are lots of examples with attributes, but this isn't maintainable. So I started searching for other solutions, however most of the information is leaning on those un-maintainable attributes I finally found "Fluent Security". What does Fluent Security offer you? Fluent Security provides a fluent interface for configuring security in ASP.NET MVC. No attributes or…

Cover Image for Install Windows 8 Consumer preview on vhd

Install Windows 8 Consumer preview on vhd

Marco Franssen

Marco Franssen /

In a previous blog post I explained to you how to install Windows 8 developer preview on vhd, so you can boot from your vhd. Since there have changed a few small things I just add an updated manual below. The installation will take about 30 minutes. Step 0 Make sure you have at least 40Gb of free disk space for your vhd. Make sure you're running Windows 7. Step 1 Download the Windows 8 consumer preview. Download the Windows 7 USB/DVD tool to make yourself a bootable usb stick. Use the tool…

Cover Image for Pitching equals invisible convincing

Pitching equals invisible convincing

Marco Franssen

Marco Franssen /

During the last year I learned and read a lot about convincing people. In this article I want to share some tricks to apply it yourself. Oh, its my first non technical article. So this will be a milestone for myself :D. It isn't always as easy to convince someone. Some people just manage to get more things done as others. A part of your skills to convince someone is in your own personality. To convince someone you have to be powerful, special and kind. This means you need to know where you're t…