LINQ Except operator and object equality?

I think I know why this fails to work as expected. Because the initial user list is a LINQ expression, it is re-evaluated each time it is iterated (once when used in GetMatchingUsers and again when doing the Except operation) and so, new user objects are created. This would lead to different references and so no matches.

Using ToList fixes this because it iterates the LINQ query once only and so the references are fixed I've been able to reproduce the problem you have and having investigated the code, this seems like a very plausible explanation. I haven't proved it yet, though Update I just ran the test but outputting the users collection before the call to GetMatchingUsers in that call, and after it. Each time the hash code for the object was output and they do indeed have different values each time indicating new objects, as I suspected Here is the output for each of the calls: Start ID=1, Name=Jeff, HashCode=39086322 ID=2, Name=Alastair, HashCode=36181605 ID=3, Name=Anthony, HashCode=28068188 ID=4, Name=James, HashCode=33163964 ID=5, Name=Tom, HashCode=14421545 ID=6, Name=David, HashCode=35567111 Start ID=1, Name=Jeff, HashCode=65066874 ID=2, Name=Alastair, HashCode=34160229 ID=3, Name=Anthony, HashCode=63238509 ID=4, Name=James, HashCode=11679222 ID=5, Name=Tom, HashCode=35410979 ID=6, Name=David, HashCode=57416410 Start ID=1, Name=Jeff, HashCode=61940669 ID=2, Name=Alastair, HashCode=15193904 ID=3, Name=Anthony, HashCode=6303833 ID=4, Name=James, HashCode=40452378 ID=5, Name=Tom, HashCode=36009496 ID=6, Name=David, HashCode=19634871 GetMatchingUsers(IEnumerable users) { IEnumerable localList = new List { new User{ ID=4, Name="James"}, new User{ ID=5, Name="Tom"} }.OfType(); OutputUsers(users); var matches = from you in users join lu in localList on u.ID equals lu.

ID select u; return matches; } public static void OutputUsers(IEnumerable users) { Console. WriteLine("==> Start"); foreach (IUser user in users) { Console. WriteLine("ID=" + user.ID.ToString() + ", Name=" + user.

Name + ", HashCode=" + user.GetHashCode().ToString()); } Console. WriteLine(" new User { ID = (int)u. Attribute("id"), Name = (string)u.

Attribute("name") } ).OfType(); //still a query, objects have not been materialized User. OutputUsers(users); var matches = User. GetMatchingUsers(users); User.

OutputUsers(users); var excludes = users. Except(matches); // excludes should contain 6 users but here it contains 8 users } }.

I think I know why this fails to work as expected. Because the initial user list is a LINQ expression, it is re-evaluated each time it is iterated (once when used in GetMatchingUsers and again when doing the Except operation) and so, new user objects are created. This would lead to different references and so no matches.

Using ToList fixes this because it iterates the LINQ query once only and so the references are fixed. I've been able to reproduce the problem you have and having investigated the code, this seems like a very plausible explanation. I haven't proved it yet, though.

Update I just ran the test but outputting the users collection before the call to GetMatchingUsers, in that call, and after it. Each time the hash code for the object was output and they do indeed have different values each time indicating new objects, as I suspected. Here is the output for each of the calls: ==> Start ID=1, Name=Jeff, HashCode=39086322 ID=2, Name=Alastair, HashCode=36181605 ID=3, Name=Anthony, HashCode=28068188 ID=4, Name=James, HashCode=33163964 ID=5, Name=Tom, HashCode=14421545 ID=6, Name=David, HashCode=35567111 Start ID=1, Name=Jeff, HashCode=65066874 ID=2, Name=Alastair, HashCode=34160229 ID=3, Name=Anthony, HashCode=63238509 ID=4, Name=James, HashCode=11679222 ID=5, Name=Tom, HashCode=35410979 ID=6, Name=David, HashCode=57416410 Start ID=1, Name=Jeff, HashCode=61940669 ID=2, Name=Alastair, HashCode=15193904 ID=3, Name=Anthony, HashCode=6303833 ID=4, Name=James, HashCode=40452378 ID=5, Name=Tom, HashCode=36009496 ID=6, Name=David, HashCode=19634871 GetMatchingUsers(IEnumerable users) { IEnumerable localList = new List { new User{ ID=4, Name="James"}, new User{ ID=5, Name="Tom"} }.OfType(); OutputUsers(users); var matches = from you in users join lu in localList on u.ID equals lu.

ID select u; return matches; } public static void OutputUsers(IEnumerable users) { Console. WriteLine("==> Start"); foreach (IUser user in users) { Console. WriteLine("ID=" + user.ID.ToString() + ", Name=" + user.

Name + ", HashCode=" + user.GetHashCode().ToString()); } Console. WriteLine(" new User { ID = (int)u. Attribute("id"), Name = (string)u.

Attribute("name") } ).OfType(); //still a query, objects have not been materialized User. OutputUsers(users); var matches = User. GetMatchingUsers(users); User.

OutputUsers(users); var excludes = users. Except(matches); // excludes should contain 6 users but here it contains 8 users } }.

Also that method returns a query as the result and not objects. Just my 2 cents... – Abhijeet Patel Mar 28 '09 at 19:45 No, because the expression is evaluated each time it is used.In my code, which shows this, it is evaluated by my output before the call to GetMatchingUsers, then again when calling GetMatchingUSers, and importantly, again during the Except. – Jeff Yates Mar 28 '09 at 19:47 Because the evaluation for GetMatchingUsers and the Except both generate their own instances, the Except fails to work as you expect.

– Jeff Yates Mar 28 '09 at 19:47 It doesn't matter if GetMatchingUsers returns a query or not, the instances returned by the users enumerable are still different on EACH evaluation. – Jeff Yates Mar 28 '09 at 19:52 I see.. so it seems that for all set related operators(extension methods) the underlying objects would have to be materialized,unless an implementation of IEquatable for the IUser interface has been passed as a parameter? – Abhijeet Patel Mar 28 '09 at 19:54.

I think you should implement IEquatable to provide your own Equals and GetHashCode methods. From MSDN (Enumerable. Except): If you want to compare sequences of objects of some custom data type, you have to implement the IEqualityComparer)>) generic interface in your class.

The following code example shows how to implement this interface in a custom data type and provide GetHashCode and Equals methods.

But the code he has should work. Why isn't it working? – Jeff Yates Mar 28 '09 at 19:18 CMS: I've implemented IEqualtable in my production code and that DOES work.

What I fail to understand is that why is it that explicitly calling ToList() on query BEFORE calling GetMatching Users produces the desired effect instead of leaving the users variable as query – Abhijeet Patel Mar 28 '09 at 19:20 Jeff: I'm not returning the IUsers from the local list I've created inside GetMatchingUser, The method returns IUsers from the original IEnumerable, so the references should still be to the original IUser objects behind the scenes so reference equality should have worked as expected! – Abhijeet Patel Mar 28 '09 at 19:27 @Abhijeet: Yes, I saw that. Hence deleting my answer.

I'm taking a closer look myself. I've reproduced what you are seeing. – Jeff Yates Mar 28 '09 at 19:29 Here are the contents of the XML file,just in case.. &l – Abhijeet Patel Mar 28 '09 at 19:32.

A) You need to override GetHashCode function. It MUST return equal values for equal IUser objects. For example: public override int GetHashCode() { return ID.GetHashCode() ^ Name.GetHashCode(); } b) You need to override object.

Equals(object obj) function in classes that implement IUser. Public override bool Equals(object obj) { IUser other = obj as IUser; if (object. ReferenceEquals(obj, null)) // return false if obj is null OR if obj doesn't implement IUser return false; return (this.ID == other.

ID) && (this. Name == other.Name); } c) As an alternative to (b) IUser may inherit IEquatable: interface IUser : IEquatable ... User class will need to provide bool Equals(IUser other) method in that case. That's all.

Now it works without calling .ToList() method.

You also have an object, db, which has as its members all the tables in the database. The table names are properties, and the type of these properties is generated by the F# compiler. The types themselves appear as nested types under dbSchema.ServiceTypes.

I cant really gove you an answer,but what I can give you is a way to a solution, that is you have to find the anglde that you relate to or peaks your interest. A good paper is one that people get drawn into because it reaches them ln some way.As for me WW11 to me, I think of the holocaust and the effect it had on the survivors, their families and those who stood by and did nothing until it was too late.

Related Questions