EntitySet.Contains – uses Reference comparisons

A gotcha that got me :)

If you’re used to using List<T> collections, and in particular the way they compare, then you need to know that an EntitySet (from LINQ to SQL) does the comparison differently…

In my experience, if I’ve created a class, for example:

 

internal class Simple 
{ 
    public int TheInt { get; set; } 
    public string TheString { get; set; } 
 
    public Simple(int theInt, string theString) 
    { 
        TheInt = theInt; 
        TheString = theString; 
    } 
} 

 

And I then add some instances of this class to a List<Simple>, if I want a ‘Contains’ call on this list to return true I would have to override the ‘Equals’ method.

For example, doing the following:

 

static void Main(string[] args) 
{ 
    List<Simple> simpleList = new List<Simple> 
                                  { 
                                      new Simple(1, "1"), 
                                      new Simple(2, "2") 
                                  }; 
 
    Simple s2 = new Simple(2, "2"); 
 
    Console.WriteLine("Contains? " + simpleList.Contains(s2)); 
    Console.WriteLine("Fin."); 
    Console.ReadLine(); 
} 

 

Will write out:

Contains? false 

If I then modify the Simple class and add the following method to it:

public override bool Equals(object obj) 
{ 
    Simple other = obj as Simple; 
    if(other == null) 
        return false; 
 
    return TheInt == other.TheInt && TheString == other.TheString; 
} 

and then run the Contains code again, I will get:

Contains? true 

Which is what I expect.

But what if we use an EntitySet?

static void Main(string[] args) 
{ 
    EntitySet<Simple> simpleEs = new EntitySet<Simple> 
                                     { 
                                         new Simple(1, "1"), 
                                         new Simple(2, "2") 
                                     }; 
 
    List<Simple> simpleList = new List<Simple> 
                                  { 
                                      new Simple(1, "1"), 
                                      new Simple(2, "2") 
                                  }; 
 
    Simple s2 = new Simple(2, "2"); 
 
    Console.WriteLine("Contains? " + simpleList.Contains(s2)); 
    Console.WriteLine("Contains ES? " + simpleEs.Contains(s2)); 
    Console.WriteLine("Fin."); 
    Console.ReadLine(); 
} 

In this case we get:

Contains? true 
Contains ES? false 

Which is not what I expect… So.. I did what any other dev would do and fired up Reflector and opened up the ‘System.Data.Linq.dll’ (c:\Program Files\Reference Assemblies\Microsoft\Framework\v3.5\), and navigated to the EntitySet, then the Contains, and following that onto ‘IndexOf’ and then into ‘entities.IndexOf’ to discover it’s doing a reference comparison:

 

public int IndexOf(T item) 
{ 
    for (int i = 0; i < this.count; i++) 
    { 
        if (this.items[i] == item) 
        { 
            return i; 
        } 
    } 
    return -1; 
} 

 

See the ‘this.items[i] == item’ comparison? That bad boy is a reference comparison, in other collections (such as the List<T>) the reference comparison is only used if the item being compared is null.

Now, in fairness, this makes sense from the perspective of LINQ to SQL, clearly, the reference is the actual one we pulled from the database, and we want it to act that way most of the time. But what if we don’t?

I’ve chosen the Extension method route:

 

internal static class EntitySetExtensions 
{ 
    public static bool ContainsUsingEquals<T>(this EntitySet<T> es, T t) 
        where T : class 
    { 
        foreach (var e in es) 
            if (e.Equals(t)) 
                return true; 
 
        return false; 
    } 
} 

 

This initially seems to maybe be inefficient, but I can see from the reflected code that the implementation of ‘IndexOf’ in the EntitySet itself is doing a for loop, so I’m not too worried. Anyhews, if we go back to our example and change the code so instead of ‘Contains’ it uses ‘ContainsUsingEquals’ instead:

Console.WriteLine("Contains ES? " + simpleEs.ContainsUsingEquals(s2)); 

We get:

Contains ES? true 

Which is what I wanted. Obviously, ‘Contains’ will still return false!

Print | posted @ Tuesday, March 10, 2009 4:03 PM

Comments on this entry:

Gravatar # re: EntitySet.Contains – uses Reference comparisons
by Kerseub at 8/4/2009 9:56 AM

Waaa. Spending almost 4 hours on this damned Equals Methos.

Thanks for this.
Post A Comment
Title:
Name:
Email:
Comment:
Verification: