3

I have two databases DB1 and DB2 where I am retrieving sales orders from each and into lists. I now want to find the sales orders that are missing in DB2 that are in DB1 so that they are added to DB2

I have listDB1 and ListDB2 in the form:

public class SalesOrder
{
      public int docNum;
      public string cardCode;
      public string cardName;
      public DateTime docDate;
      public DateTime docDueDate;                       
      public double docTotal;        
      public int lineItemCount;
      public string comments;
} 

I now want to compare the 2 lists ignoring the docNum for both lists which is auto-generated while comparing the rest of the elements. Using:

unaddedSOs = listDB1.Except(listDB2).ToList();

compares all including docNum. How do I achieve what I need so I can get the doc Numbers that are unadded?

Kinyanjui Kamau
  • 1,890
  • 10
  • 55
  • 95
  • 3
    Try implementing `IEquatable`. – Lasse V. Karlsen Apr 16 '15 at 12:27
  • 3
    "compares all including docNum", no it just compares references by default. Override `Equals` + `GetHashCode` or provide a custom `IEqualityComparer`. – Tim Schmelter Apr 16 '15 at 12:27
  • Just use a for loop. You're not gaining anything by making this more complicated than it needs to be. The LINQ will just implement a for loop under the hood anyway. – Liam Apr 16 '15 at 12:28
  • If all members should be cheched for equality, this can be achieved by making `SalesOrder` a `struct` instead of a `class`. – Codor Apr 16 '15 at 12:28
  • 1
    @TimSchmelter: Exactly, I can't see how that original version works in any way at all, unless the 2 lists contain the same references for identical items. – Baldrick Apr 16 '15 at 12:28
  • That could potentially have far reaching consequences @Codor – Liam Apr 16 '15 at 12:29
  • 1
    @Codor: Would it not still compare refs for the string members? – Baldrick Apr 16 '15 at 12:29
  • @Baldrick: i guess that it is the reason why it seems to work. He's probably adding some objects to both lists. – Tim Schmelter Apr 16 '15 at 12:30
  • 2
    @Baldrick Although `string` is a reference type, it is implemented with value semantics in the sense that checks for equality compare the content. – Codor Apr 16 '15 at 12:30
  • @Liam You are probably right, but do you think of anything particular? – Codor Apr 16 '15 at 12:31
  • [Yes](http://stackoverflow.com/questions/4274193/what-is-the-difference-between-a-mutable-and-immutable-string-in-c) [lots](http://stackoverflow.com/questions/431429/class-vs-struct-for-data-only) :) @Codor – Liam Apr 16 '15 at 12:32
  • @Codor: Spot on. Glad to fill that knowledge gap, so thanks! – Baldrick Apr 16 '15 at 12:41

1 Answers1

4

You can either implement IEquatable<SalesOrder>, or create a custom IEqualityComparer<SalesOrder>. Note i'd also advise you to turn those public fields into properties.

public class SalesOrder : IEquatable<SalesOrder>
{
    public int DocNum { get; set; }
    public string CardCode { get; set; }
    public string CardName { get; set; }
    public DateTime DocDate { get; set; }
    public DateTime DocDueDate { get; set; }
    public double DocTotal { get; set; }
    public int LineItemCount { get; set; }
    public string Comments { get; set; }

    public bool Equals(SalesOrder other)
    {
        if (ReferenceEquals(null, other)) return false;
        if (ReferenceEquals(this, other)) return true;
        return string.Equals(cardCode, other.cardCode) &&
               string.Equals(cardName, other.cardName) &&
               docDueDate.Equals(other.docDueDate) &&
               docTotal.Equals(other.docTotal) && 
               lineItemCount == other.lineItemCount &&
               string.Equals(comments, other.comments);
    }

    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj)) return false;
        if (ReferenceEquals(this, obj)) return true;
        if (obj.GetType() != this.GetType()) return false;
        return Equals((SalesOrder) obj);
    }

    public override int GetHashCode()
    {
        unchecked
        {
            var hashCode = (cardCode != null ? cardCode.GetHashCode() : 0);
            hashCode = (hashCode*397) ^ (cardName != null ? cardName.GetHashCode() : 0);
            hashCode = (hashCode*397) ^ docDueDate.GetHashCode();
            hashCode = (hashCode*397) ^ docTotal.GetHashCode();
            hashCode = (hashCode*397) ^ lineItemCount;
            hashCode = (hashCode*397) ^ (comments != null ? comments.GetHashCode() : 0);
            return hashCode;
        }
    }
Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321
  • While we are at it, this [Blog article](http://codeblog.jonskeet.uk/2006/07/28/elegant-comparisons-with-the-null-coalescing-operator/) deals with a controversial, yet suprprisingly elegant and flexible technique for customized implementations of `IEquatable`. – Codor Apr 16 '15 at 12:36
  • 1
    @Codor That article talks about `IComparable`, not `IEquatable`. [They're not the same.](http://stackoverflow.com/questions/2410101/whats-the-difference-between-icomparable-iequatable-interfaces) – Yuval Itzchakov Apr 16 '15 at 12:37
  • Indeed, I was mistaken. However I believe the approach can be generalized. – Codor Apr 16 '15 at 12:39