简体   繁体   中英

Merge Self-tracking entities

Graph of objects stored in the database and the same object graph is serialized into a binary package. Package is transmitted over the network to the client, then it is necessary to merge data from the package and data from the database.

Source code of merge:

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

And code of ImportProperties method:

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

Server save incoming changes like this :

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

When the server tries to save the changes occur exception:

AcceptChanges cannot continue because the object's key values conflict with another object in the ObjectStateManager. Make sure that the key values are unique before calling AcceptChanges.

But if I change the code of ImportProperties method, the error does not occur and the changes are saved successfully:

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

Why is there an exception?

When you transport an object graph (Entity with n-level deep navigation properties) to a client application the entities will record any changes made in their respective change trackers. When entity (or object graph) is sent back to the server side of the application basically all you need to do is:

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

I don't see the need of all the code above you posted. What are you trying to achieve with that code?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM