Why am I not getting results when performing an intersection?

Users class:

public class User  
{
    public int ID { get; set; }
    public string Email { get; set; }
}

Code:

        var usersL = new List<User>()
                        {
                            new User{ID = 1,Email = "abc@foo.com"},
                            new User{ID = 2,Email = "def@foo.com"}
                        };

        var usersR = new List<User>()
                        {
                            new User{ID = 1,Email = "abc@foo.com"},
                            new User{ID = 2,Email = "def@foo.com"}
                        };

        var both = (from l in usersL select l)
            .Intersect(from users in usersR select users);

        foreach (var r in both)
            Console.WriteLine(r.Email);

Which returns 0 results.

I know I can accomplish something similar by using join, but I want to use Intersect because A) this is eventually going to work on some DB code and we want to use this function (too long to go into why) and B) I'm just plain curious as to why Intersect isn't working here.

        var both = from l in usersL
                   join r in usersR on l.ID equals r.ID
                   select l;

Answers


.Net provides comparison logic for predefined types. In case of your join query your were joining (comparing) two IDs which were of type Int (predefined types)

var both = from l in usersL
           join r in usersR on l.ID equals r.ID
           select l;

In case of your intersect query you are trying to compare two user defined custom objects of type User. Hence you need to provide your own custom compare implementation logic.

There are 2 ways to tackle this...

Option 1: Implement the IEqualityComparer

public class User
{
    public int ID { get; set; }
    public string Email { get; set; }
}

public class MyEqualityComparer : IEqualityComparer<User>
{
    public bool Equals(User x, User y)
    {
        if (object.ReferenceEquals(x, y))
            return true;
        if (x == null || y == null)
            return false;
        return x.ID.Equals(y.ID) &&
               x.Email.Equals(y.Email);
    }

    public int GetHashCode(User u)
    {
        return new { u.ID, u.Email }.GetHashCode();
    }
}

class Program
{
    static void Main(string[] args)
    {

        var usersL = new List<User>()
                    {
                        new User{ID = 1,Email = "abc@foo.com"},
                        new User{ID = 2,Email = "def@foo.com"}
                    };

        var usersR = new List<User>()
                    {
                        new User{ID = 1,Email = "abc@foo.com"},
                        new User{ID = 2,Email = "def@foo.com"}
                    };

        var both =  (from l in usersL select l)
          .Intersect(from users in usersR select users, new MyEqualityComparer());

        foreach (var r in both)
            Console.WriteLine(r.Email);
    }
}

Option 2: Override the Equals and GetHashcode methods in the custom object itself

public class User 
{
    public int ID { get; set; }
    public string Email { get; set; }

    public override bool Equals(Object obj)
    {
        // Check for null values and compare run-time types.
        if (obj == null || GetType() != obj.GetType())
            return false;

        User x = (User)obj;
        return (ID == x.ID) && (Email == x.Email);
    }

    public override int GetHashCode()
    {
        return new { ID, Email }.GetHashCode();
    }
}

class Program
{
    static void Main(string[] args)
    {

        var usersL = new List<User>()
            {
                new User{ID = 1,Email = "abc@foo.com"},
                new User{ID = 2,Email = "def@foo.com"}
            };

        var usersR = new List<User>()
            {
                new User{ID = 1,Email = "abc@foo.com"},
                new User{ID = 2,Email = "def@foo.com"}
            };

        var both = (from l in usersL select l)
          .Intersect(from users in usersR select users);

        foreach (var r in both)
            Console.WriteLine(r.Email);
    }
}

Hope this helps.


This is in response to @sundeep who said "Now regards to your 2nd question... " (I wish you could link to comments) -- I'm just creating a new answer as I don't want to ruin the context of my original question

User class implementing IEqualityComparer

public class User : IEqualityComparer<User>
{
    public int ID { get; set; }
    public string Email { get; set; }

    public bool Equals(User x, User y)
    {
        if (object.ReferenceEquals(x, y))
            return true;
        if (x == null || y == null)
            return false;
        return x.ID.Equals(y.ID) &&
               x.Email.Equals(y.Email);
    }

    public int GetHashCode(User obj)
    {
        return new { obj.ID, obj.Email }.GetHashCode();
    }
}

Intersection returns no rows:

        var usersL = new List<User>()
                        {
                            new User{ID = 1,Email = "abc@foo.com"},
                            new User{ID = 2,Email = "def@foo.com"}
                        };

        var usersR = new List<User>()
                        {
                            new User{ID = 1,Email = "abc@foo.com"},
                            new User{ID = 2,Email = "def@foo.com"}
                        };

        var both = (from l in usersL select l)
            .Intersect(from users in usersR select users);

        foreach (var r in both)
            Console.WriteLine(r.Email);

Need Your Help

Codeigniter HMVC: I can't call "database" library inside controller

php database codeigniter hmvc

I've ran into a problem that can't seem to have an answer.

Linq row not found or changed

c# asp.net linq linq-to-sql conflict

Error Message: Row not found or changed.