Example #1
0
        protected virtual EntityEntry Merge(object detached, object persisted, NavigationEntry parentNavigation, HashSet <object> visited)
        {
            _eventManager.OnEntityMerging(detached, persisted, parentNavigation);

            EntityEntry persistedEntry = _entryServices.FindEntry(persisted) ?? _dbContext.Entry(persisted);

            visited.Add(persistedEntry.Entity);

            bool modified = Copy(detached, persistedEntry);

            foreach (NavigationEntry navigationEntry in persistedEntry.Navigations)
            {
                bool owned      = navigationEntry.Metadata.IsOwned();
                bool associated = navigationEntry.Metadata.IsAssociated();

                if (!(associated || owned))
                {
                    continue;
                }

                IEntityType        navType       = navigationEntry.Metadata.GetTargetType();
                IClrPropertyGetter getter        = navigationEntry.Metadata.GetGetter();
                object             detachedValue = getter.GetClrValue(detached);

                IEntityServices entityServices = _entityServicesFactory.GetEntityServices(navType);

                if (navigationEntry.Metadata.IsCollection())
                {
                    // a mutable list to store the result.
                    IList mergedList = Activator.CreateInstance(typeof(List <>).MakeGenericType(navType.ClrType)) as IList;

                    // create hash table for O(N) merge.
                    Dictionary <KeyValue, object> dbTable = entityServices.CreateTable((IEnumerable)navigationEntry.CurrentValue);

                    if (detachedValue != null)
                    {
                        foreach (var detachedItem in (IEnumerable)detachedValue)
                        {
                            object   persistedItem;
                            KeyValue entityKey = entityServices.GetKeyValue(detachedItem);

                            if (dbTable.TryGetValue(entityKey, out persistedItem))
                            {
                                mergedList.Add(owned
                                    ? Merge(detachedItem, persistedItem, navigationEntry, visited).Entity
                                    : persistedItem);

                                dbTable.Remove(entityKey); // remove it from the table, to avoid deletion.
                            }
                            else
                            {
                                mergedList.Add(owned ? Add(detachedItem, navigationEntry, visited).Entity
                                                     : Attach(detachedItem, navigationEntry).Entity);
                            }
                        }
                    }

                    // the rest of the items in the dbTable should be removed.
                    foreach (var dbItem in dbTable)
                    {
                        Delete(dbItem.Value, navigationEntry, visited);
                    }

                    // let EF do the rest of the work.
                    navigationEntry.CurrentValue = mergedList;
                }
                else
                {
                    if (visited.Contains(navigationEntry.CurrentValue))
                    {
                        continue;
                    }

                    if (entityServices.Equal(detachedValue, navigationEntry.CurrentValue))
                    {
                        // merge owned references and do nothing for associated references.
                        if (owned)
                        {
                            navigationEntry.CurrentValue = Merge(detachedValue, navigationEntry.CurrentValue, navigationEntry, visited).Entity;
                        }
                    }
                    else
                    {
                        if (navigationEntry.CurrentValue != null)
                        {
                            if (owned)
                            {
                                Delete(navigationEntry.CurrentValue, navigationEntry, visited);
                            }
                        }

                        if (detachedValue != null)
                        {
                            navigationEntry.CurrentValue = owned ?
                                                           Add(detachedValue, navigationEntry, visited).Entity :
                                                           Attach(detachedValue, navigationEntry).Entity;
                        }
                        else
                        {
                            // fix: if we use lazy loading we can delete correct value
                            // for example: [FK] ItemId -> Item (is null)
                            // ItemId = 1 (from RESTfull for example), Item = null
                            var fk   = navigationEntry.Metadata.ForeignKey?.Properties?.SingleOrDefault();
                            var data = fk?.GetGetter().GetClrValue(detached);
                            if (data != null && data.GetType().IsPrimitive&& !data.GetType().IsDefaultValue(data))
                            {
                                continue;
                            }

                            navigationEntry.CurrentValue = null;
                        }
                    }
                }
            }

            return(_eventManager.OnEntityMerged(detached, persistedEntry, modified, parentNavigation).EntityEntry);
        }
Example #2
0
        protected virtual EntityEntry Merge(object detached, object persisted, NavigationEntry parentNavigation, HashSet <object> visited)
        {
            var args = _eventManager.OnEntityMerging(detached, persisted, parentNavigation);

            EntityEntry persistedEntry = _entryServices.FindEntry(persisted);

            if (persistedEntry == null)
            {
                persistedEntry = _dbContext.Entry(persisted);
            }

            visited.Add(persistedEntry.Entity);

            bool modified = Copy(detached, persistedEntry);

            foreach (NavigationEntry navigationEntry in persistedEntry.Navigations)
            {
                bool owned      = navigationEntry.Metadata.IsOwned();
                bool associated = navigationEntry.Metadata.IsAssociated();

                if (!(associated || owned))
                {
                    continue;
                }

                IEntityType        navType       = navigationEntry.Metadata.GetTargetType();
                IClrPropertyGetter getter        = navigationEntry.Metadata.GetGetter();
                object             detachedValue = getter.GetClrValue(detached);

                IEntityServices entityServices = _entityServicesFactory.GetEntityServices(navType);

                if (navigationEntry.Metadata.IsCollection())
                {
                    // a mutable list to store the result.
                    IList mergedList = Activator.CreateInstance(typeof(List <>).MakeGenericType(navType.ClrType)) as IList;

                    // create hash table for O(N) merge.
                    Dictionary <KeyValue, object> dbTable = entityServices.CreateTable((IEnumerable)navigationEntry.CurrentValue);

                    if (detachedValue != null)
                    {
                        foreach (object detachedItem in (IEnumerable)detachedValue)
                        {
                            object   persistedItem;
                            KeyValue entityKey = entityServices.GetKeyValue(detachedItem);
                            if (dbTable.TryGetValue(entityKey, out persistedItem))
                            {
                                if (owned)
                                {
                                    mergedList.Add(Merge(detachedItem, persistedItem, navigationEntry, visited).Entity);
                                }
                                else
                                {
                                    mergedList.Add(persistedItem);
                                }

                                dbTable.Remove(entityKey); // remove it from the table, to avoid deletion.
                            }
                            else
                            {
                                mergedList.Add(owned ? Add(detachedItem, navigationEntry, visited).Entity
                                                     : Attach(detachedItem, navigationEntry).Entity);
                            }
                        }
                    }

                    // the rest of the items in the dbTable should be removed.
                    foreach (var dbItem in dbTable)
                    {
                        Delete(dbItem.Value, navigationEntry, visited);
                    }

                    // let EF do the rest of the work.
                    navigationEntry.CurrentValue = mergedList;
                }
                else
                {
                    if (!visited.Contains(navigationEntry.CurrentValue)) // avoid stack overflow! (this might be also done checking if the property is dependent to parent)
                    {
                        if (entityServices.Equal(detachedValue, navigationEntry.CurrentValue))
                        {
                            // merge owned references and do nothing for associated references.
                            if (owned)
                            {
                                navigationEntry.CurrentValue = Merge(detachedValue, navigationEntry.CurrentValue, navigationEntry, visited).Entity;
                            }
                        }
                        else
                        {
                            if (navigationEntry.CurrentValue != null)
                            {
                                if (owned)
                                {
                                    Delete(navigationEntry.CurrentValue, navigationEntry, visited);
                                }
                            }

                            if (detachedValue != null)
                            {
                                if (owned)
                                {
                                    navigationEntry.CurrentValue = Add(detachedValue, navigationEntry, visited).Entity;
                                }
                                else
                                {
                                    navigationEntry.CurrentValue = Attach(detachedValue, navigationEntry).Entity;
                                }
                            }
                            else
                            {
                                navigationEntry.CurrentValue = null;
                            }
                        }
                    }
                }
            }

            return(_eventManager.OnEntityMerged(detached, persistedEntry, modified, parentNavigation).EntityEntry);
        }