private bool HasDownstreamChanges(ITrackingCollection refChangeTracker, ITrackable trackableRef, ITrackable item,
                                              EntityReferenceProperty refProp, bool hasDownstreamChanges)
            {
// Get downstream changes
                IEnumerable <ITrackable> refPropItems   = refChangeTracker.Cast <ITrackable>();
                IEnumerable <ITrackable> refPropChanges = GetChanges(refPropItems);

                // Set flag for downstream changes
                bool hasLocalDownstreamChanges =
                    refPropChanges.Any(t => t.TrackingState != TrackingState.Deleted) ||
                    trackableRef.TrackingState == TrackingState.Added ||
                    trackableRef.TrackingState == TrackingState.Modified;

                // Set ref prop to null if unchanged or deleted
                if (!hasLocalDownstreamChanges &&
                    (trackableRef.TrackingState == TrackingState.Unchanged ||
                     trackableRef.TrackingState == TrackingState.Deleted))
                {
                    EntityInfo(item).RefNavPropUnchanged.Add(refProp.Property);
                    return(hasDownstreamChanges);
                }

                // prevent overwrite of hasDownstreamChanges when return from recursion
                hasDownstreamChanges |= hasLocalDownstreamChanges;
                return(hasDownstreamChanges);
            }
Beispiel #2
0
        /// <summary>
        /// Restore items marked as deleted.
        /// </summary>
        /// <param name="changeTracker">Change-tracking collection</param>
        /// <param name="visitationHelper">Circular reference checking helper</param>
        public static void RestoreDeletes(this ITrackingCollection changeTracker, ObjectVisitationHelper visitationHelper = null)
        {
            ObjectVisitationHelper.EnsureCreated(ref visitationHelper);

            // Get cached deletes
            var removedDeletes = changeTracker.GetChanges(true).Cast <ITrackable>().ToList();

            // Restore deleted items
            if (removedDeletes.Any())
            {
                var isTracking = changeTracker.Tracking;
                changeTracker.Tracking = false;
                foreach (var delete in removedDeletes)
                {
                    var items = changeTracker as IList;
                    if (items != null && !items.Contains(delete))
                    {
                        items.Add(delete);
                    }
                }
                changeTracker.Tracking = isTracking;
            }

            foreach (var item in changeTracker.Cast <ITrackable>())
            {
                // Prevent endless recursion
                if (!visitationHelper.TryVisit(item))
                {
                    continue;
                }

                // Iterate entity properties
                foreach (var navProp in item.GetNavigationProperties())
                {
                    // Process 1-1 and M-1 properties
                    foreach (var refProp in navProp.AsReferenceProperty())
                    {
                        // Get changed ref prop
                        ITrackingCollection refChangeTracker = item.GetRefPropertyChangeTracker(refProp.Property.Name);

                        // Restore deletes on rep prop
                        if (refChangeTracker != null)
                        {
                            refChangeTracker.RestoreDeletes(visitationHelper);
                        }
                    }

                    // Process 1-M and M-M properties
                    foreach (var colProp in navProp.AsCollectionProperty <ITrackingCollection>())
                    {
                        colProp.EntityCollection.RestoreDeletes(visitationHelper);
                    }
                }
            }
        }
            /// <summary>
            /// Get entities that have been added, modified or deleted, including trackable
            /// reference and child entities.
            /// </summary>
            /// <param name="items">Collection of ITrackable objects</param>
            /// <returns>Collection containing only added, modified or deleted entities</returns>
            private IEnumerable <ITrackable> GetChanges(IEnumerable <ITrackable> items)
            {
                // Prevent endless recursion by collection
                if (!visitationHelper.TryVisit(items))
                {
                    yield break;
                }

                // Prevent endless recursion by item
                items = items.Where(i => visitationHelper.TryVisit(i)).ToList();

                // Iterate items in change-tracking collection
                foreach (ITrackable item in items)
                {
                    // Downstream changes flag
                    bool hasDownstreamChanges = false;

                    // Iterate entity properties
                    foreach (var navProp in item.GetNavigationProperties())
                    {
                        // Process 1-1 and M-1 properties
                        foreach (var refProp in navProp.AsReferenceProperty())
                        {
                            ITrackable trackableRef = refProp.EntityReference;

                            // if already visited and unchanged, set to null
                            if (visitationHelper.IsVisited(trackableRef))
                            {
                                if ((trackableRef.TrackingState == TrackingState.Unchanged ||
                                     trackableRef.TrackingState == TrackingState.Deleted))
                                {
                                    EntityInfo(item).RefNavPropUnchanged.Add(refProp.Property);
                                }
                                continue;
                            }

                            // Get changed ref prop
                            ITrackingCollection refChangeTracker = item.GetRefPropertyChangeTracker(refProp.Property.Name);
                            if (refChangeTracker != null)
                            {
                                // Get downstream changes
                                IEnumerable <ITrackable> refPropItems   = refChangeTracker.Cast <ITrackable>();
                                IEnumerable <ITrackable> refPropChanges = GetChanges(refPropItems);

                                // Set flag for downstream changes
                                bool hasLocalDownstreamChanges =
                                    refPropChanges.Any(t => t.TrackingState != TrackingState.Deleted) ||
                                    trackableRef.TrackingState == TrackingState.Added ||
                                    trackableRef.TrackingState == TrackingState.Modified;

                                // Set ref prop to null if unchanged or deleted
                                if (!hasLocalDownstreamChanges &&
                                    (trackableRef.TrackingState == TrackingState.Unchanged ||
                                     trackableRef.TrackingState == TrackingState.Deleted))
                                {
                                    EntityInfo(item).RefNavPropUnchanged.Add(refProp.Property);
                                    continue;
                                }

                                // prevent overwrite of hasDownstreamChanges when return from recursion
                                hasDownstreamChanges |= hasLocalDownstreamChanges;
                            }
                        }

                        // Process 1-M and M-M properties
                        foreach (var colProp in navProp.AsCollectionProperty <IList>())
                        {
                            // Get changes on child collection
                            var trackingItems = colProp.EntityCollection;
                            if (trackingItems.Count > 0)
                            {
                                // Continue recursion if trackable hasn't been visited
                                if (!visitationHelper.IsVisited(trackingItems))
                                {
                                    // Get changes on child collection
                                    var trackingCollChanges = new HashSet <ITrackable>(
                                        GetChanges(trackingItems.Cast <ITrackable>()),
                                        ObjectReferenceEqualityComparer <ITrackable> .Default);

                                    // Set flag for downstream changes
                                    hasDownstreamChanges |= trackingCollChanges.Any();

                                    // Memorize only changed items of collection
                                    EntityInfo(item).ColNavPropChangedEntities[colProp.Property] = trackingCollChanges;
                                }
                            }
                        }
                    }

                    // Return item if it has changes
                    if (hasDownstreamChanges || item.TrackingState != TrackingState.Unchanged)
                    {
                        yield return(item);
                    }
                }
            }
        private static void MergeChanges(this ITrackingCollection originalChangeTracker,
                                         IEnumerable <ITrackable> updatedItems, ObjectVisitationHelper visitationHelper, bool isTrackableRef = false)
        {
            ObjectVisitationHelper.EnsureCreated(ref visitationHelper);

            // Process each updated item
            foreach (var updatedItem in updatedItems)
            {
                // Prevent endless recursion
                if (!visitationHelper.TryVisit(updatedItem))
                {
                    continue;
                }

                // Get matching orig item
                var origItem = originalChangeTracker.Cast <ITrackable>()
                               .GetEquatableItem(updatedItem, isTrackableRef);
                if (origItem == null)
                {
                    continue;
                }

                // Back fill entity identity on trackable ref
                if (isTrackableRef)
                {
                    var origItemIdentifiable = (IIdentifiable)origItem;
                    origItemIdentifiable.SetEntityIdentifier();
                    ((IIdentifiable)updatedItem).SetEntityIdentifier(origItemIdentifiable);
                }

                // Iterate entity properties
                foreach (var navProp in updatedItem.GetNavigationProperties())
                {
                    // Set to 1-1 and M-1 properties
                    foreach (var refProp in navProp.AsReferenceProperty())
                    {
                        ITrackingCollection refPropChangeTracker = origItem.GetRefPropertyChangeTracker(navProp.Property.Name);
                        if (refPropChangeTracker != null)
                        {
                            refPropChangeTracker.MergeChanges(new[] { refProp.EntityReference }, visitationHelper, true);
                        }
                    }

                    // Set 1-M and M-M properties
                    foreach (var colProp in navProp.AsCollectionProperty())
                    {
                        var origItemsChangeTracker = origItem.GetEntityCollectionProperty <ITrackingCollection>(navProp.Property).EntityCollection;
                        if (origItemsChangeTracker != null)
                        {
                            // Merge changes into trackable children
                            origItemsChangeTracker.MergeChanges(colProp.EntityCollection, visitationHelper);
                        }
                    }
                }

                // Set properties on orig item
                origItem.SetEntityProperties(updatedItem, originalChangeTracker);

                // Accept changes
                origItem.AcceptChanges();
            }

            // Remove cached deletes
            originalChangeTracker.RemoveCachedDeletes();
        }