2

I need to synchronize appointments of an online database and a local database. That's my code so far:

        List<Appointment> onlineAppointments = new List<Appointment>();
        List<Appointment> localAppointments = new List<Appointment>();
        Appointment appointment01 = new Appointment(new DateTime(2012, 12, 24, 17, 30, 00), new DateTime(2012, 12, 24, 17, 45, 00), name, 123, "comment", 0, "test", 123, 1, DateTime.Now);
        Appointment appointment02 = new Appointment(new DateTime(2012, 12, 24, 17, 30, 00), new DateTime(2012, 12, 24, 17, 45, 00), name, 123, "comment", 0, "test", 123, 1, DateTime.Now);

        onlineAppointments.Add(appointment01);
        localAppointments.Add(appointment02);

Since I only want to compare some properties of the object I have created an IEqualityComparer:

public class AppointmentEqualityComparer<T> : IEqualityComparer<T> where T : Appointment
{
    #region IEqualityComparer<T> Members

    public bool Equals(T x, T y)
    {
        return (x == null && y == null) || ((x != null && y != null) &&
        (x.getAppointmentStart() == y.getAppointmentStart() &&
        x.getAppointmentEnd() == y.getAppointmentEnd())
        );
    }

    /// </exception>
    public int GetHashCode(T obj)
    {
        if (obj == null)
        {
            throw new ArgumentNullException("obj");
        }

        return obj.GetHashCode();
    }

    #endregion
}

Unfortunately this does not work:

var comparer = new AppointmentEqualityComparer<Appointment>();    
IEnumerable<Appointment> diffOnlineOffline = onlineAppointments.Except(localAppointments, comparer);

Meaning diffOnlineOffline is not empty but it should be since both lists contain the same appointment.

Any idea?

6
  • I don't see any problems in the EqualityComparer. Have you gone through the execution and check the values of the two appointments when they are being compared? Commented Dec 8, 2012 at 17:37
  • Your hash code implementation should use the same properties as your equality comparison i.e. AppointmentStart and AppointmentEnd. Commented Dec 8, 2012 at 17:41
  • What do you mean, @Lee? Sorry, but I don't quite get what you mean... Could you give me a concrete example? Commented Dec 8, 2012 at 17:44
  • @libjup - Equal objects should have the same hash code, so the properties you use to determine equality should be used to calculate the hash code. Your hash code implementation uses the object hash code, so objects you want to consider equal will (probably) not have the same hash code. This is why your code does not work, since except will compare hash codes before calling your Equals method. Commented Dec 8, 2012 at 17:46
  • do you basically mean that I have to build my own hash code based on the properties I'm comparing? Does this HashCode has to be an int or can it be String as well? Also you say that my code doesn't work because they don't have the same hash code. But appointment01 = appointment02 as you can see in the signature. Or do two objects have a different hash code? Commented Dec 8, 2012 at 17:48

3 Answers 3

2

Your GetHashCode method should use the properties used for equality. At the moment, objects you want to consider equal will probably not have the same hash code.

You can use something like this:

public int GetHashCode(T obj)
{
    return 41 * (41 * (41 * (41 + obj.getAppointmentStart().GetHashCode())) 
    + obj.getAppointmentEnd().GetHashCode());
}
Sign up to request clarification or add additional context in comments.

Comments

1

I suspect the issue is with the getAppointmentStart and getAppointmentEnd methods. Here is a succinct version of your code with the same equality comparer, but using properties which works as expected:

public class Appointment
{
    private int Id { get; set; }
    public DateTime Start { get; set; }
    public DateTime End { get; set; }

    public Appointment( int id, DateTime start, DateTime end )
    {
        Start = start;
        End = end;
        Id = id;
    }
}


public class AppointmentEqualityComparer<T> : IEqualityComparer<T> where T : Appointment
{
    #region IEqualityComparer<T> Members

    public bool Equals( T x, T y )
    {
        return ( x == null && y == null )
                || ( ( x != null && y != null ) && ( x.Start == y.Start && x.End == y.End ) );
    }

    public int GetHashCode( T obj )
    {
        if( obj == null )
        {
            throw new ArgumentNullException( "obj" );
        }

        return obj.GetHashCode();
    }

    #endregion
}

And the implementation:

var onlineAppointments = new List<Appointment>();
var localAppointments = new List<Appointment>();
var appointment01 = new Appointment( 1, new DateTime( 2012, 12, 24, 17, 30, 00 ),
                                        new DateTime( 2012, 12, 24, 17, 45, 00 ) );
var appointment02 = new Appointment( 2, new DateTime( 2012, 12, 24, 17, 30, 00 ),
                                        new DateTime( 2012, 12, 24, 17, 45, 00 ) );

onlineAppointments.Add( appointment01 );
localAppointments.Add( appointment02 );

var comparer = new AppointmentEqualityComparer<Appointment>();
var diffOnlineOffline = onlineAppointments.Except( localAppointments, comparer ).ToList();

Where diffOnlineOffline only shows the first appointment. This leads to the conclusion that the getAppointmentStart and getAppointmentEnd methods are returning values other than the actual date used in the constructor.

5 Comments

Thanks for your answer! But it still does not work :( foreach (Appointment appointment in diffOnlineOffline) { MessageBox.Show("at least 1 appointment in list"); } is still executed ...
Found out how it works. Just needed to implement @Lee's solution as well... but thanks anyways!!!
How many appointments are you expecting in diffOnlineOffline based on the 2 sample lists?
0 since my appointments were the same; but now it's working perfectly... thanks!
Equality comparers should always ensure that the hashcode is equal for two objects that are equal and make it unlikely that the hascode is equal for two objects that are not equal (according to the comparer) you code manages the latter but unfortunately not the former which is way your code will fail the same ways as OP
0

You need to implement IEqualityComparer in your Appointment class

1 Comment

You can pass an EqualityComparer to Except (which is what's happening in OP) so no it's not necessary to implement IEqualityComaprer in the domain class

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.