0

I have a PM system that is more like the Google Mail style. I mean messages are grouped into conversations. If a user receives a message, it shows up in their inbox. Also, the this user sends a message to someone who in turn replies back, then this message also shows up in their inbox.

Some messages are being retrieved in both queries which end up being grouped into one List. I tried to remove duplicates by doing what Jon Skeet did in removing duplicates from a list C# but still, I keep getting duplicates. Here's my code:

UPDATED:

public class Message : IEquatable<Message>
{
    public int Id { get; set; }
    [MaxLength(150)]
    public string Subject { get; set; }
    [MaxLength(3000)]
    public string Content { get; set; }
    public DateTime DateSent { get; set; }
    public DateTime? LastViewed { get; set; }
    public bool IsRead { get; set; }
    public bool DisplayInInbox { get; set; }
    public virtual User SentBy { get; set; }
    public virtual User ReceivedBy { get; set; }
    public int? ParentId { get; set; }

    public override bool Equals(object other)
    {
        return Equals(other as Message);
    }

    public bool Equals(Message other)
    {
        if (object.ReferenceEquals(other, null))
        {
            return false;
        }
        if (object.ReferenceEquals(other, this))
        {
            return true;
        }
        return Id == other.Id;
    }

    public override int GetHashCode()
    {
        return this.Id;
    }
}

//inside MessagingService public IList GetThreads(User user) { //Get all messages that are not replies. var tmp = _repository.GetMany(c => c.DisplayInInbox.Equals(true) && c.ParentId.Equals(null)); var threads = (from c in tmp where GetReplies(user, c.Id).Count() > 0 select c).ToList(); var threadsByUser = user.ReceivedMessages.Where(m => m.DisplayInInbox.Equals(true) && m.ParentId.Equals(null)).ToList(); threads.AddRange(threadsByUser); threads.Distinct().ToList(); return threads; }

Am I doing something wrong here?

Community
  • 1
  • 1
Kassem
  • 8,116
  • 17
  • 75
  • 116

3 Answers3

2

In both DTO's you have implemented GatHashcode. Shouldn't you be using these when testing equality?

return Id == other.Id && Subject == other.Subject && SentBy.Nickname == other.SentBy.Nickname &&
           DateSent == other.DateSent;

return Id == other.Id && Subject == other.Subject && Sender == other.Sender;

become

return GetHashCode() == other.GetHashCode()

EDIT:

I'm also slightly being a biff... don't override/overload the equals method. It is the equals method that uses GetHashcode to determine equality. You have overloaded equals to catch the DTO, which by default would have compared the result of GetHashcode on both objects. Your overloaded version does not compare the hashcode, making it redundant, when actually it looks like a correct implementation.

EDIT 2 (in response to code changes in your post): It's hard to tell because the code section at the bottom of your post is not formatted, but the second to last line:

threads.Distinct().ToList();
return threads;

This doesn't do anything. Merge the two:

return threads.Distinct().ToList();
Smudge202
  • 4,689
  • 2
  • 26
  • 44
  • I removed the code from the DTO's. Now everything lies in the entity classes. – Kassem Apr 26 '11 at 14:32
  • If removing the equals overload doesn't resolve your issue, then the problem is with your hashcode, and subsequently subtle differences between the fields. @A.R. is probably the closest answer (+1 to you A.R.) – Smudge202 Apr 26 '11 at 14:50
  • I'm actually using @A.R. implementation now. I'm really surprised it still doesn't work. Could it be the `.Distinct()` call not doing what it is supposed to do? – Kassem Apr 26 '11 at 14:56
  • @Kassem I've edited my answer. Pretty much ignore the first part... see after EDIT 2. – Smudge202 Apr 26 '11 at 15:44
  • +1!! That actually did it! I thought the Distinct() method does its job in place, guess not... Thanks for the help, appreciated :) – Kassem Apr 26 '11 at 15:58
2

Without providing us any example of your duplicate messages that are not being removed, I am going to guess that your usage of 'DateTime' in your equality checking code is a likely culprit. It is very common, and easy to write something like 'SentDate = DateTime.Now;' and have that confuse the system later on down the line. That is just a guess though.

Meanwhile I would suggest that your equality and hashcode functions are way way into the territory of overkill. Now I am going to assume that the 'ID' for both the Message and the MessageThread classes are supposed to be unique, in which case you can really simpify your logic, and more easily get to the bottom of your problem, like so;

public override bool Equals(object other)
{
    return Equals(other as Message);
}

public bool Equals(Message other)
{
  if (other == null) { return false; }
  return this.Id == other.Id;
}

public override int GetHashCode()
{
  // If the ID is unique, then it satisfied the purpose of 'GetHashCode'
  return this.Id;
}

Obviously you will want to do this for your other classes as well. The benefit here is that there are less moving parts, and consequently less opportunity for something to go awry. Also, the message bodies, senders, etc. are not needed for you to determine the uniqueness of the message.

A.R.
  • 15,405
  • 19
  • 77
  • 123
  • I changed my code to use your implementation and I debugged it very carefully. I think the comparison code is not the problem. Could it be the .Distinct() that is not doing its job? I also tried to use .Union() but that excluded one of the messages while it was not supposed to. The code currently returns three messages (threads), which if it was working correctly it should be returning only 2 because two of those have the same ID (value = 1). – Kassem Apr 26 '11 at 14:45
0

Is this ok?

return Id == other.Id && Subject == other.Subject && SentBy.Nickname == other.SentBy.Nickname &&
               DateSent == other.DateSent;

I would prefer approach that if id equals, mail messages is same:

return Id == other.Id;
VikciaR
  • 3,324
  • 20
  • 32