c# - Diferrence between two lists with nested values -


i have variables of particular type, classes them :

public class user {     public int? id { get; set; }     public string password { get; set; }     public string firstname { get; set; }     public string lastname { get; set; }     public string email { get; set; }     public list<accessiblesite> sites { get; set; } }   public class accessiblesite {     public int id { get; set; }     public string name { get; set; }     public list<role> roles { get; set; }     public bool ischecked { get; set; } }   public class role {     public int id { get; set; }     public string name { get; set; }     public bool ischecked { get; set; }     public string userfriendlyname { get; set; } } 

a user can have many accessiblesite under him , each site has list of role associated it.

there 2 objects of user type. need find difference between 2 lists.

so far code have written comparing non list objects correctly.

static class extentions {     public static list<variance> detailedcompare<t>(this t val1, t val2)     {         list<variance> variances = new list<variance>();         list<fieldinfo> fi = val1.gettype().getruntimefields().tolist<fieldinfo>();     foreach (fieldinfo f in fi)     {         variance v = new variance();         v.prop = f.name;         v.vala = f.getvalue(val1);         v.valb = f.getvalue(val2);         if (v.vala system.collections.ilist && v.vala.gettype().isgenerictype)         {             foreach (var listitem in v.vala.detailedcompare(v.valb))             {                 variances.add(listitem);             }          }         else         {             if ((v.vala != null && v.valb != null) && !v.vala.equals(v.valb))                 variances.add(v);         }      }     return variances; }     class variance  {      public string prop { get; set; }      public object vala { get; set; }      public object valb { get; set; }       public string changedfromtext      {           {          return "value of " + prop + " has changed " + vala + " " + valb;      }  } 

the items in list needs compared on basis of id.if stuff has changed or not.

i looked @ following solution need me create 2 diferrent classes , each accessiblesite , roles. there way can write using 1 solution. recursively maybe ? tia

the solution involved.

here untested solution whipped up. i'll leave test , adjust it:

this class attribute used specify field or property on class should treated id value of instances of class.

[attributeusage(attributetargets.class)] public class idpropertyattribute : attribute {     public string idproperty { get; private set; }     public idpropertyattribute(string idproperty) { this.idproperty = idproperty; } } 

the textensions class contains 2 extension methods , number of supporting methods used reflecting , analyzing values between 2 object graphs.

public static class textensions {      public static variance detailedcompare<t>(this t val1, t val2)     {         return  val1.detailedcompare(val2, null);     }      public static variance detailedcompare<t>(this t val1, t val2, string fieldname)     {         return  typeof(ienumerable).isassignablefrom(typeof(t))                 ?   textensions.enumerablecompare(val1, val2, fieldname)                 :   typeof(t).isprimitive                     ?   textensions.valuecompare(val1, val2, fieldname)                     :   textensions.objectcompare(val1, val2, fieldname);     }      private static variance valuecompare<t>(t val1, t val2, string fieldname)     {         return  val1 != null && val2 != null && !val1.equals(val2)                 ?   new variance.valuevariance<t>() { prop = fieldname??"<root>", vala = val1, valb = val2 }                 :   null;     }      private static variance objectcompare<t>(t val1, t val2, string fieldname)     {         var variance = new variance.enumerablevariance() {prop = fieldname};          list<fieldinfo> fi = val1.gettype().getruntimefields().tolist<fieldinfo>();         foreach (fieldinfo f in fi)         {             var subvariance = f.getvalue(val1).detailedcompare(f.getvalue(val2), f.name);              if (subvariance != null)    variance.variances.add(subvariance);         }         return variance;     }      private static variance enumerablecompare<t>(t val1, t val2, string fieldname)     {         return  typeof(ienumerable<>).isassignablefrom(typeof(t))                 ?   textensions.homogeneousenumerablecompare<t>(val1, val2, fieldname)                 :   textensions.heterogeneousenumerablecompare<t>(val1, val2, fieldname);     }      private static variance heterogeneousenumerablecompare<t>(t val1, t val2, string fieldname)     {         throw new notimplementedexception();     }      private static variance homogeneousenumerablecompare<t>(t val1, t val2, string fieldname)     {         var subtype     = typeof(t).getgenericarguments()[0];         return  typeof(keyvaluepair<,>).isassignablefrom(subtype)                 ?   textensions.homogeneouskeyvalueenumerablecompare(subtype, val1, val2, fieldname)                 :   subtype.isprimitive                     ?   homogeneousvalueenumerablecompare(subtype, val1, val2, fieldname)                     :   homogeneousobjectenumerablecompare(subtype, val1, val2, fieldname);     }      private static variance homogeneousobjectenumerablecompare<t>(type subtype, t val1, t val2, string fieldname)     {         var submethod   =   typeof(textensions)                             .getmethod("typedhomogeneousobjectenumerablecompare", bindingflags.static, null, new []{typeof(t), typeof(t), typeof(string)}, null)                             .makegenericmethod(new []{typeof(t), subtype});          return (variance) submethod.invoke(null, new object[] {val1, val2, fieldname});     }      private static variance typedhomogeneousobjectenumerablecompare<t, tsubtype>(t val1, t val2, string fieldname)     {         var idattribute = typeof(tsubtype).getcustomattribute<idpropertyattribute>(true);         return  idattribute == null                 ?   textensions.keylesstypedhomogeneousobjectenumerablecompare<t, tsubtype>(val1, val2, fieldname)                 :   textensions.keyedtypedhomogeneousobjectenumerablecompare<t, tsubtype>(idattribute, val1, val2, fieldname);     }      private static variance keylesstypedhomogeneousobjectenumerablecompare<t, tsubtype>(t val1, t val2, string fieldname)     {         var list1 = (ienumerable<tsubtype>) val1;         var list2 = (ienumerable<tsubtype>) val2;         return  list1.count() != list2.count()                 ?   new variance.keylessobjectenumerablevariance() { prop = fieldname, listacount = list1.count(), listbcount = list2.count() }                 :   null;     }      private static variance keyedtypedhomogeneousobjectenumerablecompare<t, tsubtype>(idpropertyattribute idattribute, t val1, t val2, string fieldname)     {         var idmember    = typeof(tsubtype).getmember(idattribute.idproperty).firstordefault();         if (idmember == null) throw new idmembernotfoundexception(idattribute.idproperty, typeof(tsubtype).fullname);          var submethod   =   typeof(textensions)                             .getmethod("subtypedkeyedtypedhomogeneousobjectenumerablecompare", bindingflags.static, null, new []{typeof(t), typeof(t), typeof(string)}, null)                             .makegenericmethod(new []{typeof(t), typeof(tsubtype), (idmember propertyinfo ? ((propertyinfo) idmember).propertytype : ((fieldinfo) idmember).fieldtype)});          return (variance) submethod.invoke(null, new object[] {val1, val2, fieldname, idmember});     }      private static variance subtypedkeyedtypedhomogeneousobjectenumerablecompare<t, tsubtype, tsubtypekey>(t val1, t val2, string fieldname, memberinfo idmember)     {         return  subtypedkeyedtypedhomogeneousobjectenumerablecomparewithkeyfunc<t, tsubtype, tsubtypekey>                 (                     val1,                     val2,                     fieldname,                     idmember propertyinfo                     ?   new func<tsubtype, tsubtypekey>(item=>(tsubtypekey) ((propertyinfo) idmember).getvalue(item))                     :   item=>(tsubtypekey) ((fieldinfo) idmember).getvalue(item)                 );     }      private static variance subtypedkeyedtypedhomogeneousobjectenumerablecomparewithkeyfunc<t, tsubtype, tsubtypekey>(t val1, t val2, string fieldname, func<tsubtype, tsubtypekey> getkey)     {         var set1        = ((ienumerable<tsubtype>) val1).todictionary(a=>getkey(a));         var set2        = ((ienumerable<tsubtype>) val2).todictionary(a=>getkey(a));          return textensions.dictionarycompare<tsubtypekey, tsubtype>(set1, set2, fieldname);     }      private static variance homogeneousvalueenumerablecompare<t>(type subtype, t val1, t val2, string fieldname)     {         var submethod   =   typeof(textensions)                             .getmethod("typedhomogeneousvalueenumerablecompare", bindingflags.static, null, new []{typeof(t), typeof(t), typeof(string)}, null)                             .makegenericmethod(new []{typeof(t), subtype});          return (variance) submethod.invoke(null, new object[] {val1, val2, fieldname});     }      private static variance typedhomogeneousvalueenumerablecompare<t, tsubtype>(t val1, t val2, string fieldname)     {         var variance    = new variance.enumerablevariance();         var list1       = ((ienumerable<tsubtype>) val1).tolist();         var list2       = ((ienumerable<tsubtype>) val1).tolist();          foreach(var item in list1)  if (!list2.contains(item))  variance.variances.add(new variance.valueremovedvariance<tsubtype>() {prop = fieldname, value = item});         foreach(var item in list2)  if (!list1.contains(item))  variance.variances.add(new variance.valueaddedvariance<tsubtype>() {prop = fieldname, value = item});          return variance;     }      private static variance homogeneouskeyvalueenumerablecompare<t>(type subtype, t val1, t val2, string fieldname)     {         var keytype     = subtype.getgenericarguments()[0];         var valuetype   = subtype.getgenericarguments()[1];         var submethod   =   typeof(textensions)                             .getmethod("typedhomogeneouskeyvalueenumerablecompare", bindingflags.static, null, new []{typeof(t), typeof(t), typeof(string)}, null)                             .makegenericmethod(new []{typeof(t), keytype, valuetype});          return (variance) submethod.invoke(null, new object[] {val1, val2, fieldname});     }      private static variance typedhomogeneouskeyvalueenumerablecompare<t, tsubtypekey, tsubtypevalue>(t val1, t val2, string fieldname)     {         var set1        = ((ienumerable<keyvaluepair<tsubtypekey, tsubtypevalue>>) val1).todictionary(a=>a.key, a=>a.value);         var set2        = ((ienumerable<keyvaluepair<tsubtypekey, tsubtypevalue>>) val2).todictionary(a=>a.key, a=>a.value);          return textensions.dictionarycompare<tsubtypekey, tsubtypevalue>(set1, set2, fieldname);     }      private static variance dictionarycompare<tsubtypekey, tsubtypevalue>(dictionary<tsubtypekey, tsubtypevalue> set1, dictionary<tsubtypekey, tsubtypevalue> set2, string fieldname)     {         var variance    = new variance.enumerablevariance();          foreach(var key in set1.keys)         {             var subvariance =   !set2.containskey(key)                                  ?   new variance.keyedobjectremovedvariance<tsubtypekey>() {prop = fieldname, key = key}                                 :   set1[key].detailedcompare(set2[key], fieldname + "[" + key.tostring() + "]");              if (subvariance != null) variance.variances.add(subvariance);         }          foreach(var key in set2.keys)  if (!set1.containskey(key))  variance.variances.add(new variance.keyedobjectremovedvariance<tsubtypekey>() {prop = fieldname, key = key});          return variance;     }  } 

this exception needed indicate class has been decorated idattribute, field or property specified not found in class during analysis.

public class idmembernotfoundexception : applicationexception {     public idmembernotfoundexception(string membername, string typename) : base(membername + " not found in type " + typename +  ".") {} } 

the variance class has been broken out base class several sub classes represent various types of variances can occur.

public abstract class variance {     public string prop { get; set; }      public string getchangedfromtext() { return this.getchangedfromtext(null); }     protected abstract string getchangedfromtext(string parent);      public class valuevariance<t> : variance     {         public object vala { get; set; }         public object valb { get; set; }          protected override string getchangedfromtext(string parent)         {             return "value of " + (parent??"<root>") + "." + this.prop + " has changed " + this.vala + " " + this.valb;         }     }      public class enumerablevariance : variance     {         public list<variance> variances   = new list<variance>();          protected override string getchangedfromtext(string parent)         {             stringbuilder   returnstring    = new stringbuilder();             foreach(var variance in this.variances)             {                 returnstring.append(variance.getchangedfromtext(this.prop));             }              return returnstring.tostring();         }     }      public class keylessobjectenumerablevariance : variance     {         public int listacount;         public int listbcount;         protected override string getchangedfromtext(string parent)         {             return "count of keyless items " + (parent??"<root>") + "." + this.prop + " has changed " + this.listacount.tostring() + " " + this.listbcount.tostring();         }     }      public class valueremovedvariance<t> : variance     {         public t value;         protected override string getchangedfromtext(string parent)         {             return "value " + this.value.tostring() + " of " + (parent??"<root>") + "." + this.prop + " removed.";         }     }      public class valueaddedvariance<t> : variance     {         public t value;         protected override string getchangedfromtext(string parent)         {             return "value " + this.value.tostring() + " of " + (parent??"<root>") + "." + this.prop + " added.";         }     }      public class keyedobjectremovedvariance<t> : variance     {         public t key;         protected override string getchangedfromtext(string parent)         {             return "key " + this.key.tostring() + " of " + (parent??"<root>") + "." + this.prop + " removed.";         }     }      public class keyedobjectaddedvariance<t> : variance     {         public t key;         protected override string getchangedfromtext(string parent)         {             return "key " + this.key.tostring() + " of " + (parent??"<root>") + "." + this.prop + " added.";         }     }  } 

your variances can include:

  • two primitive values differ.
  • value items in 1 heterogeneous list missing second list.
  • value items missing in 1 heterogeneous present in second list.
  • keyed object items in 1 heterogeneous list missing second list.
  • keyed object items missing in 1 heterogeneous present in second list.
  • object items have been matched between 2 heterogeneous lists have variances.
  • value items in 1 homogeneous list missing second list.
  • value items missing in 1 homogeneous present in second list.
  • keyed object items in 1 homogeneous list missing second list.
  • keyed object items missing in 1 homogeneous present in second list.
  • object items have been matched between 2 homogeneous lists have variances.
  • two object items have variances

the code above should address variances. may find other cases. gives starting point.


Comments

Popular posts from this blog

html - Firefox flex bug applied to buttons? -

html - Missing border-right in select on Firefox -

c# - two queries in same method -