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
Post a Comment