2

Ih, i am facing a problem with IEquatable (C#). As you can see in the following code, I got a class where i've implement IEquatable but it's "Equals" method is not getting reach. My objective is: I have a datetime column in my database and i would like to distinct only date, not considering the "time" part.

for example: 12-01-2014 23:14 would be equal to 12-01-2014 18:00.

namespace MyNamespace
{
    public class MyRepository
    {
        public void MyMethod(int id)
        {
            var x = (from t in context.MyTable
                     where t.id == id
                     select new MyClassDatetime()
                     {
                         Dates = v.Date
                     }).Distinct().ToList();
        }
    }


public class MyClassDatetime : IEquatable<MyClassDatetime>
{
    public DateTime? Dates { get; set; }

    public bool Equals(MyClassDatetime other)
    {
        if (other == null) return false;
        return (this.Dates.HasValue ? this.Dates.Value.ToShortDateString().Equals(other.Dates.Value.ToShortDateString()) : false);
    }

    public override bool Equals(object other)
    {
        return this.Equals(other as MyClassDatetime );
    }

    public override int GetHashCode()
    {
        int hashDate = Dates.GetHashCode();
        return hashDate;
    }
}
}

Have you know how can i make it work properly or other option to do what i need?? Thank you!!

Soner Gönül
  • 97,193
  • 102
  • 206
  • 364
  • 1
    "Two objects that are equal return hash codes that are equal." http://msdn.microsoft.com/en-us/library/system.object.gethashcode(v=vs.110).aspx – dee-see Jul 08 '14 at 14:44

2 Answers2

8

Your implementation of GetHashCode is incorrect for the desired equality semantics. That's because it returns different hash codes for dates that you want to compare equal, which is a bug.

To fix it, change it to

public override int GetHashCode()
{
    return Dates.HasValue ? Dates.Value.Date.GetHashCode() : 0;
}

You should also update Equals in the same spirit, it's not a good idea to mess with string representations of dates:

public bool Equals(MyClassDatetime other)
{
    if (other == null) return false;
    if (Dates == null) return other.Dates == null;
    return Dates.Value.Date == other.Dates.Value.Date;
}

Update: As usr very correctly points out, since you are using LINQ on an IQueryable the projection and Distinct call will be translated to a store expression and this code will still not run. To get around that you can use an intermediate AsEnumerable call:

var x = (from t in context.MyTable
         where t.id == id
         select new MyClassDatetime()
         {
             Dates = v.Date
         }).AsEnumerable().Distinct().ToList();
Community
  • 1
  • 1
Jon
  • 428,835
  • 81
  • 738
  • 806
  • This code will not be called because he's executing an EF query. – usr Jul 08 '14 at 14:48
  • @usr: It will be called just fine. The query results are being projected into `MyClassDatetime` instances and `.Distinct()` happens afterwards. – Jon Jul 08 '14 at 14:49
  • This is Queryable.Distinct, though. Will be translated. – usr Jul 08 '14 at 14:49
  • @usr: Oh my. You are right. I 've edited the answer to address this. – Jon Jul 08 '14 at 15:15
0

Thans for reply but it still not solving my problem.

I finally found a way to do it but without using IEquatable.

var x = (from t in context.MyTable where t.Id == id select EntityFunctions.CreateDateTime(t.Date.Value.Year, t.Date.Value.Month,t.Date.Value.Day, 0, 0, 0)).Distinct();

=)