2015-08-19 3 views
1

я должен переменных определенного типа, классы для них являются:Diferrence между двумя списками с вложенными значениями

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; } 
} 

Пользователь может иметь много AccessibleSite под ним, и каждый сайт имеет список Role, связанный с Это.

Существует два объекта типа User. Мне нужно найти разницу между этими двумя списками.

Пока что код, который я написал, правильно сравнивает объекты, отличные от списка.

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 is 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 
    { 
    get 
    { 
     return "value of " + Prop + " has changed from " + valA + " to " + valB; 
    } 
} 

Элементы в списке необходимо сравнивать на основе ID.if, изменилось или нет.

Я посмотрел на следующее solution, но мне нужно было создать два разных класса, каждый из которых будет использоваться для доступа и ролей. Можно ли написать это, используя только одно решение. Может быть, рекурсивно? TIA

+0

Является ли порядок элементов в списках значительным? Делает большую разницу в решении! –

+0

Я не получил вопрос. Скажем, в первом объекте я обнаружил AccessibleSite Id = 1. Во втором объекте пользователя я нахожу доступный сайт с тем же идентификатором, а затем сравниваю другие значения функции AccessibleSite. То же самое с объектом ролей – SJMan

ответ

1

Решение для чего-то подобного очень востребовано.

Вот непроверенное решение, которое я взбивал. Я оставлю это для проверки и настройки:

Этот атрибут класса используется для указания того, какое поле или свойство класса следует рассматривать как значение Id экземпляров класса.

[AttributeUsage(AttributeTargets.Class)] 
public class IdPropertyAttribute : Attribute 
{ 
    public string IdProperty { get; private set; } 
    public IdPropertyAttribute(string idProperty) { this.IdProperty = idProperty; } 
} 

Класс TExtensions содержит два метода расширения и ряд вспомогательных методов, используемых для отражения и анализа значений между двумя графиками объектов.

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 is 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 is 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; 
    } 

} 

Это исключение необходимо указать, что класс был украшен IdAttribute, но поле или свойство, указанное не было найдено в классе во время анализа.

public class IdMemberNotFoundException : ApplicationException 
{ 
    public IdMemberNotFoundException(string memberName, string typeName) : base(memberName + " was not found in type " + typeName + ".") {} 
} 

Класс дисперсии был развернут в базовый класс с несколькими субом классов для представления различных типов отклонений, которые могут произойти.

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 from " + this.valA + " to " + 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 from " + this.listACount.ToString() + " to " + 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 + " was 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 + " was 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 + " was 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 + " was added."; 
     } 
    } 

} 

Ваши отклонения могут включать в себя:

  • Два примитивные значения отличаются.
  • Величины в одном разнородном списке отсутствуют во втором списке.
  • Величины элементов отсутствуют в одном разнородном, которые присутствуют во втором списке.
  • Элементы с ключевыми объектами в одном гетерогенном списке отсутствуют во втором списке.
  • Объекты с ключевыми объектами отсутствуют в одном разнородном состоянии, которые присутствуют во втором списке.
  • Объекты, которые были сопоставлены между двумя гетерогенными списками, имеют отклонения.
  • Величины в одном однородном списке отсутствуют во втором списке.
  • Элементы ценности отсутствуют в одном однородном состоянии, которые присутствуют во втором списке.
  • Элементы с ключевыми объектами в одном однородном списке отсутствуют во втором списке.
  • Элементы с ключевыми объектами отсутствуют в одном однородном состоянии, которые присутствуют во втором списке.
  • Объекты, которые были сопоставлены между двумя однородными списками, имеют отклонения.
  • два элемента объекта имеет отклонения

Код выше, следует надеяться решить эти отклонения. Вы можете найти другие случаи. Надеюсь, это даст вам хорошую отправную точку.

Смежные вопросы