2013-09-17 2 views
0

У меня есть требование скопировать только заполненные значения из одного объекта, которые еще не заполнены другим объектом того же типа.Копировать ТОЛЬКО заполненные значения из одного объекта, которые еще не заполнены другим объектом того же типа

Например, нам передан объект, он частично создается экземплярами данных, мы читаем базу данных, чтобы получить полностью экземплярную версию объекта - однако это может не иметь изменений приложением, переданным в базу данных еще - следовательно нам нужно перенести любые значения из версии базы данных в переданную в версии объекта - без перезаписи каких-либо значений, которые могут уже существовать в переданном объекте (так как это самые последние значения).

Код, предложенный Адамом Робинсоном в другом сообщении (см. Ниже, очень полезен - спасибо!) Является хорошей отправной точкой. Однако мне нужно расширить это, поскольку я хочу только скопировать значения, которые еще не заполнены целевым объектом (т. Е. Нужно проверить, что destProperty не является нулевым). Однако, как добавленное усложнение, внутри объекта передаются внутренние сложные типы, этот код копирует подгруппы высокого уровня, не вдаваясь в индивидуальные свойства подгрупп (т.е. любые переменные, объявленные с корневым cdt, я могу попробовать и проверьте значение null, но все поля в sub cdts просто копируются без прохождения отдельных полей).

Любая помощь была бы принята с благодарностью.

public static void CopyPropertyValues(object source, object destination) 
{ 
    var destProperties = destination.GetType().GetProperties(); 

foreach (var sourceProperty in source.GetType().GetProperties()) 
{ 
    foreach (var destProperty in destProperties) 
    { 
     if (destProperty.Name == sourceProperty.Name && 
    destProperty.PropertyType.IsAssignableFrom(sourceProperty.PropertyType)) 
     { 
      destProperty.SetValue(destination, sourceProperty.GetValue(
       source, new object[] { }), new object[] { }); 

      break; 
     } 
    } 
} 

}

+0

Являются ли «источник» и «пункт назначения» того же класса или возможно, что они будут отличаться? Являются ли они объектами в Entity Framework? –

+0

Источник и назначение одного класса, они не должны отличаться! Благодаря! – user2026086

+0

Я бы отфильтровал ваши свойства следующим образом: '.GetProperties(). Где (свойство => property.CanRead && property.CanWrite)'. В противном случае вы можете попытаться прочитать свойства только для установки или написать свойства get-only. – dcastro

ответ

0

Прежде всего, необходимо определить, что заселена средства в вашей книге. Как только вы это определите, напишите собственную версию следующего метода IsDefaultValue. То, что я написал ответит true следующим образом:

  • если это bool то оно должно иметь значение false
  • если это int то она должна быть 0
  • если это любой class затем должен быть null
  • т.д.

Так вот моя версия метода:

public static bool IsDefaultValue(object @object) { 
    if (null == @object) 
     return true; 
    else if (@object.GetType().IsValueType) { 
     var isDefault = Activator.CreateInstance(@object.GetType()).Equals(@object); 
     return isDefault; 
    } else 
     return false; 
} 

Тогда, предполагая, что вы заинтересованы только в не свойств индексатор, что у вас нет иерархий и что вы всегда будете вызывать этот метод с объектами одного и того же типа , , вы можете просто отфильтровать те свойства, которые по умолчанию не заданы в source, но по умолчанию находятся в destination.

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

Пожалуйста, не то, что я написал здесь, просто демонстрация того, как вы могли бы выполнить свою задачу. В вашем конкретном сценарии есть сложные детали, которые вам нужно решать сами, поскольку они не очевидны из вашего вопроса.

Например, я предположил, что это было бы хорошей идеей, чтобы добавить условие останова для рекурсивного обхода (параметр remainingDepth)

public static void CopyPropertyValues(object source, object destination, int remainingDepth = 3) { 
    // we've reached the farthest point we're allowed to go to 
    // anything beyond this point won't be affected by this method 
    if (remainingDepth == 0) 
     return; 
    // just a check to make sure the following lines won't backfire 
    if ((null == source) || (null == destination)) 
     throw new ArgumentNullException(); 

    // we'll need to also check that the 2 objects are of the same type 
    var type = source.GetType(); 
    if (destination.GetType() != type) 
     throw new ArgumentException("The two objects should be of the same type"); 

    var properties = type.GetProperties() 
     // just filter out the properties which are indexers (if any) 
     // and also those properties which are read or write only 
     .Where(property => (property.GetIndexParameters().Length == 0) && 
          property.CanRead && property.CanWrite); 

    foreach (var property in properties) { 
     var sourceValue = property.GetValue(source, null); 
     var destValue = property.GetValue(destination, null); 

     if (!IsDefaultValue(sourceValue)) 
      if (IsDefaultValue(destValue)) 
       property.SetValue(destination, sourceValue, null); 
      else 
       if (sourceValue.GetType() == destValue.GetType()) 
        CopyPropertyValues(sourceValue, destValue, remainingDepth - 1); 
    } 

} 

Пожалуйста, обратите внимание, что свойство перечисления требуется только один раз, так как объекты (как вы сказали это сами в разделе комментариев) имеют один и тот же тип.

Также будьте осторожны с отражением, когда производительность важна.

+0

Спасибо, что нашли время, чтобы помочь, я буду экспериментировать с вашими предложениями! – user2026086

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