Объединить объекты с самостоятельным отслеживанием

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

Исходный код слияния:

        //objList - data from package
        var objectIds = objList.Select(row => row.ObjectId).ToArray();

        //result - data from Database
        var result = SomeService.Instance.LoadObjects(objectIds);

        foreach (var OSobj in objList)
        {
            var obj = result.Objects.ContainsKey(OSobj.ObjectId)
                ? result.Objects[OSobj.ObjectId]
                : result.Objects.CreateNew(OSobj.ObjectId);

            var targetObject = result.DataObjects.Where(x => x.ObjectId == OSobj.ObjectId).FirstOrDefault();

            targetObject.StopTracking();
            var importedProperties = ImportProperties(targetObject.Properties, OSobj.Properties);
            targetObject.Properties.Clear();
            foreach (var property in importedProperties)
            {
                targetObject.Properties.Add(property);
            }
            targetObject.StartTracking();
        }

        return result;

И код метода ImportProperties:

static List<Properties> ImportProperties(
        IEnumerable<Properties> targetProperties,
        IEnumerable<Properties> sourceProperties)
    {
        Func<Guid, bool> hasElement = targetProperties
            .ToDictionary(e => e.PropertyId, e => e)
            .ContainsKey;


        var tempTargetProperties = new List<Properties>();
        foreach (var sourceProperty in sourceProperties)
        {
            if (!hasElement(sourceProperty.PropertyId))
            {
                sourceProperty.AcceptChanges();
                tempTargetProperties.Add(sourceProperty.MarkAsAdded());
            }
            else
            {
                sourceProperty.AcceptChanges();
                tempTargetProperties.Add(sourceProperty.MarkAsModified());
            }
        }

        return tempTargetProperties;
    }

Сервер сохраняет входящие изменения следующим образом:

_context.ApplyChanges("OSEntities.Objects", entity);
_context.SaveChanges(SaveOptions.DetectChangesBeforeSave);

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

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

Но если я изменяю код метода ImportProperties, ошибка не возникает и изменения успешно сохраняются:

static List<Properties> ImportProperties(
        IEnumerable<Properties> targetProperties,
        IEnumerable<Properties> sourceProperties)
    {
        Func<Guid, bool> hasElement = targetProperties.ToDictionary(e => e.PropertyId, e => e).ContainsKey;


        var tempTargetProperties = new List<Properties>();
        foreach (var sourceProperty in sourceProperties)
        {
            if (!hasElement(sourceProperty.PropertyId))
            {
                var newProp = new Properties
                                  {
                                      ElementId = sourceProperty.ElementId,
                                      Name = sourceProperty.Name,
                                      ObjectId = sourceProperty.ObjectId,
                                      PropertyId = sourceProperty.PropertyId,
                                      Value = sourceProperty.Value
                                  };

                tempTargetProperties.Add(newProp);
            }
            else
            {
                var modifiedProp = new Properties
                                       {
                                           ElementId = sourceProperty.ElementId,
                                           Name = sourceProperty.Name,
                                           ObjectId = sourceProperty.ObjectId,
                                           PropertyId = sourceProperty.PropertyId,
                                           Value = sourceProperty.Value
                                       };

                modifiedProp.MarkAsModified();
                tempTargetProperties.Add(modifiedProp);
            }
        }

        return tempTargetProperties;
    }

Почему есть исключение?


person Gepard_vvk    schedule 17.05.2012    source источник
comment
Что вы подразумеваете под слиянием пакета и базы данных при передаче пакета клиенту? Я вижу слияние только при передаче новых, обновленных или удаленных объектов на сервер. В Entity Framework есть два простых метода, о которых вы уже упоминали: context.ApplyChanges() и context.SaveChanges(). Имхо, помимо этих двух методов, не так уж и много нужно.   -  person Bernoulli IT    schedule 21.05.2012
comment
Граф объектов хранится в базе данных и этот же граф объектов сериализуется в бинарный пакет. Когда мы передаем пакет клиенту, данные, которые мы использовали, могут измениться. Эти изменения между пакетом и базой данных должны быть объединены.   -  person Gepard_vvk    schedule 25.05.2012


Ответы (1)


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

try
{
  using(Entities context = new Entities())
  {
    context.ApplyChanges(someEntity);
    context.SaveChanges();
  }
}
catch
{
  ...
}

Я не вижу необходимости во всем приведенном выше коде. Чего вы пытаетесь достичь с помощью этого кода?

person Bernoulli IT    schedule 29.05.2012
comment
Вы не понимаете сути проблемы. Граф объектов сериализован в бинарный пакет. Пакет передается клиенту. Клиент получает данные из пакета! Поэтому я не могу использовать ваш код - на самом деле этот граф не содержится в репозитории на сервере. Если передать этот график на сервер и использовать метод ApplyChanges() - возникает ошибка... - person Gepard_vvk; 04.06.2012
comment
Если вы так говорите... возможно, вы правы, тогда я ничем не могу вам помочь, извините. На мой взгляд, вы все слишком усложняете. - person Bernoulli IT; 05.06.2012