Issues with InsertOrUpdateGraph

Dec 18, 2014 at 2:15 PM
We have run into some issues using InsertOrUpdateGraph. For example given the following:
            var testOrder = new Order
            {
                ObjectState = ObjectState.Added,
                OrderDate = DateTime.Now,
                BillToAddress = new Address
                {
                    ObjectState = ObjectState.Added,
                    Addr1 = "BillTo"
                },
                OrderFields = new List<OrderField>
                    {
                        new OrderField
                        {
                            ObjectState = ObjectState.Added,
                            Key = "OF1-1",
                            Value = "OF1-1"

                        },
                        new OrderField
                        {
                            ObjectState = ObjectState.Added,
                            Key = "OF1-2",
                            Value = "OF1-2"

                        }
                    },
                OrderLineItems = new List<OrderLineItem>()
                    {
                        new OrderLineItem
                        {
                            ObjectState = ObjectState.Added,
                            ShipToAddress = new Address
                            {
                                ObjectState = ObjectState.Added,
                                Addr1 = "ShipTo1"
                            },
                            LineNumber = 1,
                            Name = "OL1"
                        },
                        new OrderLineItem
                        {
                            ObjectState = ObjectState.Added,
                            ShipToAddress = new Address
                            {
                                ObjectState = ObjectState.Added,
                                Addr1 = "ShipTo2"
                            },
                            LineNumber = 2,
                            Name = "OL2"
                        }
                    },
                PurchasingOrganizationId = currentPurchasingOrganizations[0].PurchasingOrganizationId
            };
            OrderRepository.InsertOrUpdateGraph(testOrder);
            UnitOfWork.SaveChanges();
Results in an error of:
Attaching an entity of type 'OrderForge.Entities.Models.OrderField' failed because another entity of the same type already has the same primary key value. This can happen when using the 'Attach' method or setting the state of an entity to 'Unchanged' or 'Modified' if any entities in the graph have conflicting key values. This may be because some entities are new and have not yet received database-generated key values. In this case use the 'Add' method or the 'Added' entity state to track the graph and then set the state of non-new entities to 'Unchanged' or 'Modified' as appropriate.
The models in question are using Guids for Primary Keys and are all Guid.Empty prior to the call to InsertOrUpdateGraph. After digging into this it looks like the old InsertGraph was just a call to .Add. Commenting out the SyncObjectGraph in InsertOrUpdateGraph does allow the order and children to be created as expected.

After looking at the code for SyncObjectGraph, I am having a hard time understanding its intended purpose.
            // Set tracking state for child collections
            foreach (var prop in entity.GetType().GetProperties())
            {
                // Apply changes to 1-1 and M-1 properties
                var trackableRef = prop.GetValue(entity, null) as IObjectState;
                if (trackableRef != null && trackableRef.ObjectState == ObjectState.Added)
                {
                    _dbSet.Attach((TEntity)entity);
                    _context.SyncObjectState((IObjectState)entity);
                }

                // Apply changes to 1-M properties
                var items = prop.GetValue(entity, null) as IList<IObjectState>;
                if (items == null) continue;

                foreach (var item in items)
                    SyncObjectGraph(item);
            }
If I am understanding this correctly, it is going through all of the properties in the entity being inserted and if any of them have and object state of Added, it is attaching the main entity to the DbSet<TEntity>. I guess I am confused as to what purpose re-attaching the entity over and over is serving. Since the issue I am having is a result of Attach being used versus Add, I went with the assumption that perhaps this code was intended to be attaching all of the property entities not the main entity and modified it as follows:
                // Apply changes to 1-1 and M-1 properties
                var trackableRef = prop.GetValue(entity, null) as IObjectState;
                if (trackableRef != null && trackableRef.ObjectState == ObjectState.Added)
                {
                    var dbContext = _context as DbContext;
                    var dbSet = dbContext.Set(prop.PropertyType);

                    dbSet.Attach(prop.GetValue(entity, null));
                    _context.SyncObjectState((IObjectState)entity);
                }
This seems to have worked for the limited test cases I have so far. I was hoping for some input on the issue we saw and the solution I have come up so far.
Dec 18, 2014 at 7:05 PM
After digging into this a bit more, I noticed the 1-M properties where never getting attached and where only being inserted by the call to _dbSet.Add(entity); after SynchObjectGraph is called. There was a previous discussion on this issue that I updated but changing the code for the 1-M to the following
                // Apply changes to 1-M properties
                var items = prop.GetValue(entity, null) as ICollection;
                if (items == null) continue;

                foreach (var item in items)
                    SyncObjectGraph(item);
causes the child lists to be attached. Unfortunately this brings my back to the same error that I referenced above.
Coordinator
Dec 20, 2014 at 1:55 AM
Marked as answer by lelong37 on 12/19/2014 at 5:55 PM