3

I would like to compare two classes that look like this

public class Order
{
        public string KundNummer, KundNamn, ErReferens, VarReferens, KontraktsNummer, Betvillk, Levvillk, Levsatt, Speditor,
            Resenh, Projekt, OrderDatum, LeveransDatum, ErtOrdernr, LagerbokfDatum, KundPostAdr, KundPostAdr2, KundGLN, KundPostnr, 
            KundOrt, KundLandKod, KundLand, KundVATnr, KundDistrikt, KundSaljare, KundText1, KundText2, KundText3, KundSprak,
            KundValuta, KundRabattAvtal, KundRabattAvtalBenamning, KundPrislista, KundPrislistaBenamning, KundALnamn, KundALpostAdr, KundALpostAdr2, KundALGLN, KundALpostnr, KundALort,
            KundALlandKod, KundALland;
        public double OrderNummer, Fakturarabatt, Frakt, Expavg, Brutto, Netto, ExklMoms, Totalt, Moms, Avrundn, KundValutaKurs, KundValutaEnhet;
        public int EUPeriodSamman, InklMoms, EjKlar, Levererad, Malukerad, BestallningSkapad, Ordererk,
            Plocklista, Foljesedel, ExtraOrderdokument, Restorder, Faktura, KundSparaText, KundExport, KundRantefakturering, KundKravbrev,
            KundKravavgift, KundRestnoteraEj, KundSamlingsfakturera;
    }

i want to compare two objects of this class for logging which fields in my database that changed.

public string OrderUppdateraOrder(Order order)
    {
        Order OrderToCompare = new Order();
        OrderToCompare = OrderVisaOrderInformation(order.OrderNummer);

        //then the code goes on to make the changes to the database from the class order
        //while OrdertoComapre still have the values from before this function was called
    }

So is it possible to loop through these classes and compare them or do I have to write an if for every variable in my class? :)

Or maybe convert it to a List? I don't know? :P

Thank you for answers

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Dendei
  • 551
  • 1
  • 7
  • 18
  • 1
    This might help you: http://stackoverflow.com/questions/6240385/can-i-use-reflection-to-get-an-existing-variable-by-providing-its-name However, that's bad practice. – looper Nov 21 '12 at 09:46
  • You can always use reflection and compare each property on the instances to the others. But depending on what ORM you are using, you might have built in support for finding everything that has changed. To use refleciton check my blog post: http://blog.filipekberg.se/2011/10/08/exploring-reflection-finding-a-value-in-any-of-the-objects-properties/ *Remember though that using Reflection can cause performance penelties!* – Filip Ekberg Nov 21 '12 at 09:46
  • my God... is there any possibility of your this class to get into smaller component classes... rather a normalized form... :) – bonCodigo Nov 21 '12 at 09:47
  • perhaps like this: http://stackoverflow.com/questions/986572/hows-to-quick-check-if-data-transfer-two-objects-have-equal-properties-in-c/986617#986617 ? (noting that that is property-based - would need minor tweaks to work with fields) – Marc Gravell Nov 21 '12 at 09:47
  • thank you i will check the sites :) – Dendei Nov 21 '12 at 09:47
  • You want to check that every field in one instance of an `order` class is equal to another instance of another instance of an `order`? – LukeHennerley Nov 21 '12 at 09:48
  • So if I understand this you have an instance of the order object and want to compare the information in your instance with the information in your database? How do you communicate with your db, linq, entity, sql? – Eric Herlitz Nov 21 '12 at 09:49
  • It's entirely clear exactly what you want to do here. Do you want to see if *any* field is different and in that case get an indication that "something has changed", or do you want to compare every single field and log all differences in some way? The most straight-forward way to do it is indeed with reflection, but the exact implementation differens depending on what results you want to achieve. – Anders Arpi Nov 21 '12 at 09:49
  • bonCodigo: I dont know :) its for visma api program and i need one variable for each field the user inputs – Dendei Nov 21 '12 at 09:49
  • Anders Holmström: i want to log if there is a difference and log the difference something like this CustomerNumber: changed from 456 to 345 – Dendei Nov 21 '12 at 09:51
  • 1
    I HAVE to mention this: Please use better variable names, for your own sake and the sake of your colleagues. Using abbreviations in variable names is most of the time a really bad habit. When you look at your code in months again, can you remember what all your abbreviations mean? Just use better variable names, it comes with no penalty except of having to write a bit more (which is hardly an argument with a good editor). – user1793714 Nov 21 '12 at 09:51
  • 2
    user1793714: i named them like the fields are named in the swedish version of: visma administration 2000. – Dendei Nov 21 '12 at 09:53
  • Perhaps in his language or client's, these variables make sense. However it's important for readability and managing your code later - even it's your or by someone else. – bonCodigo Nov 21 '12 at 09:54
  • but you might be right but i have documented everything in an word file :) – Dendei Nov 21 '12 at 09:54
  • 1
    Now that you've clarified the purpose, perhaps you could adapt [this](http://stackoverflow.com/questions/3060382/comparing-2-objects-and-retrieve-a-list-of-fields-with-different-values) to allow for fields – Marc Gravell Nov 21 '12 at 09:59
  • 1
    @Dendei, Just a side-note: Not that it matters but is this homework? If not, you might want to store "Kund" (Customer in Swedish) as a reference on the Order instead of storing all the fields on it. – Filip Ekberg Nov 21 '12 at 10:06
  • @Filip Ekberg im a intern :) but yes my customer class is just as big with only customer fields, i have some in order to because in visma the ordercustomer fields are not the same as the customer fields. i prob could split both up and make one new class containing the ones they have in common. And split my functions dealing with them :) – Dendei Nov 21 '12 at 10:25

3 Answers3

3

i want to log if there is a difference and log the difference something like this CustomerNumber: changed from 456 to 345

The best option is probably just to use reflection to crawl over the public properties and fields, comparing them. This will have less code to compare, but will have a performance overhead. You can greatly reduce that overhead using tools like FastMember:

static class Program {
    static void Main()
    {
        Order o1 = new Order { Resenh = "abc" },
              o2 = new Order { Resenh = "abc" };
        ShowDiffs(o1, o2); // {nothing}
        o2.Resenh = "def";
        ShowDiffs(o1, o2); // Resenh changed from abc to def
    }
    static void ShowDiffs<T>(T x, T y)
    {
        var accessor = TypeAccessor.Create(typeof(T));
        if (!accessor.GetMembersSupported) throw new NotSupportedException();
        var members = accessor.GetMembers();

        foreach (var member in members)
        {
            object xVal = accessor[x, member.Name],
                   yVal = accessor[y, member.Name];
            if (!Equals(xVal, yVal))
            {
                Console.WriteLine("{0} changed from {1} to {2}",
                    member.Name, xVal, yVal);
            }
        }
    }
}
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • @LukeHennerley Yes... it would. The generic type can be inferred from the arguments supplied to the method call. – Anders Arpi Nov 21 '12 at 10:13
  • this looks like exactly what i want but im just checking up on things i dont understand so well. so bear with me i you are waiting for solved mark ^^ – Dendei Nov 21 '12 at 10:31
  • is TypeAccessor from FastMember? cant find much on it? i'll test it :) – Dendei Nov 21 '12 at 10:46
  • 1
    @Dendei yes, that is from FastMember - basically, it is a small utility library I wrote that wraps up the most common reflection operations, and bundles in some meta-programming for performance – Marc Gravell Nov 21 '12 at 10:51
  • @Dendei you could do exactly the same thing with `typeof(T).GetFields(...)`, and `someField.GetValue(obj)` - it would just be much slower – Marc Gravell Nov 21 '12 at 10:52
1

Maybe this approach can help you

public class Order
{
    public string KundNummer, KundNamn, ErReferens, VarReferens, KontraktsNummer, Betvillk, Levvillk, Levsatt, Speditor,
        Resenh, Projekt, OrderDatum, LeveransDatum, ErtOrdernr, LagerbokfDatum, KundPostAdr, KundPostAdr2, KundGLN, KundPostnr,
        KundOrt, KundLandKod, KundLand, KundVATnr, KundDistrikt, KundSaljare, KundText1, KundText2, KundText3, KundSprak,
        KundValuta, KundRabattAvtal, KundRabattAvtalBenamning, KundPrislista, KundPrislistaBenamning, KundALnamn, KundALpostAdr, KundALpostAdr2, KundALGLN, KundALpostnr, KundALort,
        KundALlandKod, KundALland;
    public double OrderNummer, Fakturarabatt, Frakt, Expavg, Brutto, Netto, ExklMoms, Totalt, Moms, Avrundn, KundValutaKurs, KundValutaEnhet;
    public int EUPeriodSamman, InklMoms, EjKlar, Levererad, Malukerad, BestallningSkapad, Ordererk,
        Plocklista, Foljesedel, ExtraOrderdokument, Restorder, Faktura, KundSparaText, KundExport, KundRantefakturering, KundKravbrev,
        KundKravavgift, KundRestnoteraEj, KundSamlingsfakturera;

    public static bool operator ==(Order left, Order right)
    {
        foreach (var field in left.GetType().GetFields())
        {
            object valueLeft = field.GetValue(left);
            object valueRight = field.GetValue(right);

            if (!object.Equals(valueLeft, valueRight))
                return false;
        }

        return true;
    }

    public static bool operator !=(Order left, Order right)
    {
        return !(left == right);
    }

}
joseangelmt
  • 2,018
  • 18
  • 32
  • `object.Equals(valueLeft,valueRight)` would be more appropriate than using a comparer and checking for 0; also, if you see the comments, the intent is a memberwise breakdown of changes – Marc Gravell Nov 21 '12 at 10:07
1

Ok from how I understand you - you have a method which will retreive an Order object from the database and an Order object which you may have intially got from a database, but modified. You want to check if one, or many orders have change without saying:

For each order if Database.A equals Your.A etc.

I would put your check into one place and override the Equals method.

  public class Order
  {
    public int Id;
    public string SomeProp;
    public string AnotherProp;
    public override bool Equals(object obj)
    {
      Order orderToCompare = (Order)obj;
      return (SomeProp == orderToCompare.SomeProp && AnotherProp == orderToCompare.AnotherProp);
    }
  }

Then have two methods, one for updating a single order and one for updating many orders.

private void UpdateOrder(Order o)
{
  //Get the corresponding order from the database (I suspect Entity Framework here in order to get an object?)
  Order dbOrder = OrderVisaOrderInformation(o.Id);
  if (dbOrder.Equals(o))
  {
    //Do some update
  }
}
private void UpdateManyOrders(List<Order> orders)
{
  var dbOrders = (from order in orders
                  select OrderVisaOrderInformation(order.Id));
  List<Order> ordersToUpdate = dbOrders.Where(x => !x.Equals(orders.First(y => y.Id == x.Id))).ToList();
  foreach (Order orderToUpdate in ordersToUpdate)
  {
    //Update the order
  }
}

This may be wrong but from how I understand your question, this is what you want to do.

LukeHennerley
  • 6,344
  • 1
  • 32
  • 50
  • If you see the comments, the requirement is a memberwise breakdown of changes. Also: if you override `Equals` you should also override `GetHashCode` - and for this many members that is high-maintenance – Marc Gravell Nov 21 '12 at 10:09
  • @MarcGravell I see... I started doing this answer whilst the many comments were happening. Thanks. – LukeHennerley Nov 21 '12 at 10:12
  • I can relate... I have a deleted answer doing an optimized `Expression` comparison ;p – Marc Gravell Nov 21 '12 at 10:13
  • sorry for if im slow at answering im just checking through all the posts :) but im not sure, the order object in public string OrderUppdateraOrder(Order order) function is what the user writes in to change in the database. And before the changes take place i make an same object with the values before the changes take place so i can compare and log the differences. so one object has the values the database have right now and one have the values that will overwrite the current ones in the database. hope this makes sense – Dendei Nov 21 '12 at 10:16