6

I'm trying to do a query that does not include repeated IdUser values, ​​but does not work.

this is my linq query:

var sql= (from u in db.USER
          join c in db.CONSULT on u.IdUser equals c.IdUser 
          select new UsuersViewModel 
                 {  
                    IdUser = c.IdUser, 
                    DateCreate=c.DateCreate, 
                    IdTypeConsult = c.IdTypeConsult, 
                    Sex=u.Sex 
                 })
                 .Distinct();

I want this:

SELECT   distinct CONSULT.IdUser , CONSULT.DateCreate, 
         CONSULT.IdTypeConsult , USER.Sex
FROM   CONSULT INNER JOIN
       USER ON CONSULT.IdUser = USER.IdUser 

The query give duplicated records

Why doesn't it work?

Shiva
  • 20,575
  • 14
  • 82
  • 112
kalu
  • 337
  • 2
  • 9
  • 19
  • 1
    You're not giving it anything to 'distinct' on. :) – Only Bolivian Here Jul 26 '12 at 16:16
  • 4
    Please expand on "Does not work"... Does it give you duplicates? Does it return nothing? Does the non-distinct set (before the `Distinct()`) contain what you'd expect? Without knowing the problem its hard to guess what's going wrong... – Chris Jul 26 '12 at 16:17
  • Yes, as @OnlyBolivianHere says, in the SQL statement, you are stating that CONSULT.IdUser should be distinct, in your LINQ, you are stating that each whole record must be distinct – stevethethread Jul 26 '12 at 16:19
  • 1
    @OnlyBolivianHere: What do you mean? Surely he has an Ienumerable that is being distincted... – Chris Jul 26 '12 at 16:19
  • 1
    @stevethethread: That is not how distinct works in SQL. It refers to the whole recordset (much like in `select top 10 a, b, c` the `top 10` refers to the whole dataset). – Chris Jul 26 '12 at 16:20
  • @Chris. Of course. Stupid me! Must have been staring at the screen too long today, in 30 degrees heat!! – stevethethread Jul 26 '12 at 16:21
  • @stevethethread: I know what you mean. We're just not used to the heat in this country are we? ;-) – Chris Jul 26 '12 at 16:22
  • Ha, indeed. Mind you, all over tomorrow!!! – stevethethread Jul 26 '12 at 16:23
  • @Chris the query give duplicated records :( – kalu Jul 26 '12 at 16:25
  • @kalu Duplicated on what basis? Do you mean you are getting more than one of the same IdUser? If so, then one of the other fields in your distinct is different and you need to figure out the problem in your data. As I mentioned above; it's difficult to resolve without sample data. – Bert Jul 26 '12 at 16:41
  • @Bert Evans give duplicated records of `IdUser` – kalu Jul 26 '12 at 16:58

3 Answers3

7

I think you want to use the Distinct(IEqualityComparer<T>) overload. You need to create an IEqualityComparer to do what you want:

class UserComparer : IEqualityComparer<UsuersViewModel >
{
    public bool Equals(UsuersViewModel  x, UsuersViewModel y)
    {
        //Check whether the compared objects reference the same data.
        if (Object.ReferenceEquals(x, y)) return true;

        //Check whether any of the compared objects is null.
        if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null))
            return false;

        return x.IdUser == y.IdUser;
    }

    // If Equals() returns true for a pair of objects 
    // then GetHashCode() must return the same value for these objects.

    public int GetHashCode(UsuersViewModel  user)
    {
        //Check whether the object is null
        if (Object.ReferenceEquals(user, null)) return 0;

        return user.IdUser == null ? 0 : user.IdUser.GetHashCode();
    }
}

Then use it like this:

var comparer = new UserComparer();
var sql= (from u in db.USER
          join c in db.CONSULT on u.IdUser equals c.IdUser 
          select new UsuersViewModel 
                 {  
                    IdUser = c.IdUser, 
                    DateCreate=c.DateCreate, 
                    IdTypeConsult = c.IdTypeConsult, 
                    Sex=u.Sex 
                 })
                 .Distinct(comparer);

I'm not sure if that will generate the SQL you want, but will likely get the results you want.

Peter Ritchie
  • 35,463
  • 9
  • 80
  • 98
  • Edited the code; but, it assumes you mispelled User as Usuers. If that's not the case, replace "User" with "Usuers" – Peter Ritchie Jul 26 '12 at 16:50
  • I can't help much if I don't know what the error details are. – Peter Ritchie Jul 26 '12 at 16:57
  • Pay particular note to my comment about "User" and "Usuers", if you ignored that you will get an error. – Peter Ritchie Jul 26 '12 at 16:58
  • `Error 1 Argument 1: cannot convert from 'System.Linq.IQueryable' to ' System.Linq.ParallelQuery'` – kalu Jul 26 '12 at 17:18
  • Really is `UsuersViewModel`, please See my Edit Question – kalu Jul 26 '12 at 17:42
  • I'm not sure what is so hard about replacing "User" with whatever you want; but, I've edited the post with "UsuersViewModel", whatever "usuers" is.... – Peter Ritchie Jul 26 '12 at 17:46
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/14476/discussion-between-kalu-and-peter-ritchie) – kalu Jul 26 '12 at 17:49
  • 1
    Keep in mind that if you are using Linq to Entities, this will throw an error because the result is not a vanilla IEnumerable but, rather, an IQueryable; so you will need to add .AsEnumerable() before .Distinct(comparer). – Bradley Mountford Aug 19 '13 at 12:52
2

When comparing class instances (vs. anonymous types) you need to define "equality". For anonymous types the compiler assumes that equality means "all fields are equal" like SQL does. So you have a few choices:

  1. Use an anonymous type in your query, use .Distinct(), and convert to a strong type afterwards,
  2. Define an IEqualityComparer<Usuers> class and pass that to Distinct,
  3. Override Equals (and GetHashCode) in Usuers

2) and 3) will be very similar code. 2) is more flexible (you can define equality in different ways by defining different classes, while 3) will be used whenever you compare Uusers insatnces (not just in this query).

See my answer to a similar problem here.

Community
  • 1
  • 1
D Stanley
  • 149,601
  • 11
  • 178
  • 240
  • I hadn't even noticed that the original code was using a defined type rather than an anonymous one. It was only the second time of re-reading *after* reading this post that I realised. Good job on spotting that. :) – Chris Jul 26 '12 at 16:30
0

Note: The following can be run in LinqPad (free at http://www.linqpad.net/) - simply set the Language dropdown to "C# Program" and paste the code into the editor window.

You can use "group" to provide your distinct requirement as follows:

void Main()
{
    var db = new DataBase();
    var sql= (from u in db.USER
          join c in db.CONSULT on u.IdUser equals c.IdUser 
          group c by new { c.IdUser, c.DateCreate, c.IdTypeConsult, u.Sex } into gc
          select new UsuersViewModel 
                 {  
                    IdUser = gc.Key.IdUser, 
                    DateCreate=gc.Key.DateCreate, 
                    IdTypeConsult = gc.Key.IdTypeConsult, 
                    Sex=gc.Key.Sex 
                 })
                 .Distinct();
    sql.Dump("SQL Distinct Demo");
}
public class Consultation {
    public int  IdUser {get;set;}
    public DateTime DateCreate {get;set;}
    public int IdTypeConsult {get;set;}
}
public class UsuersViewModel : Consultation {
    public string Sex {get;set;}
}
public class DataBase {
    public IEnumerable<Consultation> CONSULT {
        get { 
            return new List<Consultation>{
                new Consultation { IdUser = 1, DateCreate=DateTime.Today, IdTypeConsult = 2},
                new Consultation { IdUser = 2, DateCreate=DateTime.Today.AddDays(1), IdTypeConsult = 4}
            };
        }}
    public IEnumerable<UsuersViewModel> USER {
        get {
            return new List<UsuersViewModel>{
                new UsuersViewModel { IdUser = 1, Sex="M"},
                new UsuersViewModel { IdUser = 1, Sex="M"},
                new UsuersViewModel { IdUser = 2, Sex="F"},
                new UsuersViewModel { IdUser = 2, Sex="F"}
            };
        }}
}

The following is the result:

BillKrat
  • 291
  • 2
  • 6