Рекурсивное отсоединение объекта в структуре объекта

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

public void DetachRec(object objectToDetach)
{
  DetachRec(objectToDetach, new HashSet<object>());
}

// context is my ObjectContext
private void DetachRec(object objectToDetach, HashSet<object> visitedObjects)
{
  visitedObjects.Add(objectToDetach);

  if (objectToDetach == null)
  {
    return;
  }
  Type type = objectToDetach.GetType();
  PropertyInfo[] propertyInfos = type.GetProperties();
  foreach (PropertyInfo propertyInfo in propertyInfos)
  {
    // on to many
    if (propertyInfo.PropertyType.IsGenericType && propertyInfo.PropertyType.GetGenericTypeDefinition() == typeof(ICollection<>))
    {

      if (context.Entry(objectToDetach).Collection(propertyInfo.Name).IsLoaded)
      {

        var propValue = (IEnumerable) propertyInfo.GetValue(objectToDetach, null);

        if (propValue != null)
        {
          var subcontext = new List<object>();

          foreach (var subObject in propValue)
          {
            subcontext.Add(subObject);
          }
          foreach (var subObject in subcontext)
          {
            if (!visitedObjects.Contains(subObject))
            {
              context.DetachRecursive(subObject, visitedObjects);
            }
          }
        }
      }
    }
    // (many to one)
    else if (propertyInfo.PropertyType.Assembly == type.Assembly)
    {
        //the part to detach many-to-one objects
    }
  }

  context.Detach(objectToDetach);
}

person Jimy Weiss    schedule 31.03.2017    source источник
comment
Когда вы говорите, что коллекции удалены, что это значит? Означает ли это, что свойство возвращает null?   -  person JuanR    schedule 31.03.2017
comment
Прочтите stackoverflow.com/a/7693732/430661 и посмотрите, применимо ли это.   -  person Alex Paven    schedule 31.03.2017
comment
@Juan, да, это означает, что свойство возвращает значение null.   -  person Jimy Weiss    schedule 31.03.2017
comment
Перезагрузить после отсоединения   -  person T.S.    schedule 31.03.2017
comment
@Т.С. Можете ли вы дополнить мой код, чтобы понять, что вы имеете в виду?   -  person Jimy Weiss    schedule 31.03.2017
comment
context.Refresh(....) – узнайте, как это может отменить ваши изменения   -  person T.S.    schedule 31.03.2017
comment
@Т.С. зачем отменять мои изменения, я явно не устанавливаю для свойства значение null. Это происходит путем выполнения context.Detach(objectToDetach).   -  person Jimy Weiss    schedule 31.03.2017
comment
Конечно, ответ Ивана, как обычно, великолепен, но мне любопытно, как это можно использовать? Разве вы не можете получить сущности, используя Include и AsNoTracking? Интересно, почему объекты сначала должны быть присоединены, а затем отсоединены для любой последующей обработки. Никогда не нуждался в этом до сих пор, но это может не иметь большого значения.   -  person Gert Arnold    schedule 01.04.2017


Ответы (1)


Перед отсоединением элементов коллекции вы можете сохранить значение коллекции и установить для свойства коллекции значение null. Таким образом вы предотвратите удаление EF элементов коллекции, когда элементы и/или родительский объект отсоединены. В конце процесса вы просто восстановите эти значения обратно.

Начните с добавления следующего using в файл исходного кода:

using System.Data.Entity.Infrastructure;

Измените реализацию общедоступного метода следующим образом:

public void DetachRec(object objectToDetach)
{
    var visitedObjects = new HashSet<object>();
    var collectionsToRestore = new List<Tuple<DbCollectionEntry, object>>();
    DetachRec(objectToDetach, visitedObjects, collectionsToRestore);
    foreach (var item in collectionsToRestore)
        item.Item1.CurrentValue = item.Item2;
}

сигнатура закрытого рекурсивного метода для:

private void DetachRec(object objectToDetach, HashSet<object> visitedObjects, List<DbCollectionEntry, object>> collectionsToRestore)

и тело блока один ко многим if для:

var collectionEntry = context.Entry(objectToDetach).Collection(propertyInfo.Name);
if (collectionEntry.IsLoaded)
{
    var collection = collectionEntry.CurrentValue;
    if (collection != null)
    {
        collectionsToRestore.Add(Tuple.Create(collectionEntry, collection));
        collectionEntry.CurrentValue = null;
        foreach (var item in (IEnumerable)collection)
        {
            if (!visitedObjects.Contains(item))
            {
                DetachRec(item, visitedObjects, collectionsToRestore);
            }
        }
    }
}

и все готово.

person Ivan Stoev    schedule 31.03.2017