/// <summary>
 /// Traverse an object graph to populate null reference properties.
 /// </summary>
 /// <param name="context">Used to query and save changes to a database</param>
 /// <param name="item">Object that implements IHasTrackingState</param>
 public static void LoadRelatedEntities(this DbContext context, IHasTrackingState item)
 {
     context.TraverseGraph(item, n =>
     {
         if (n.Entry.State == EntityState.Detached)
         {
             n.Entry.State = EntityState.Unchanged;
         }
         foreach (var reference in n.Entry.References)
         {
             if (!reference.IsLoaded)
             {
                 reference.Load();
             }
         }
     });
 }
 /// <summary>
 /// Traverse an object graph asynchronously to populate null reference properties.
 /// </summary>
 /// <param name="context">Used to query and save changes to a database</param>
 /// <param name="item">Object that implements IHasTrackingState</param>
 public static async Task LoadRelatedEntitiesAsync(this DbContext context, IHasTrackingState item)
 {
     await context.TraverseGraphAsync(item, async n =>
     {
         if (n.Entry.State == EntityState.Detached)
         {
             n.Entry.State = EntityState.Unchanged;
         }
         foreach (var reference in n.Entry.References)
         {
             if (!reference.IsLoaded)
             {
                 await reference.LoadAsync();
             }
         }
     });
 }
Example #3
0
        public void ApplyChanges(IHasTrackingState root)
        {
            ChangeTracker.TrackGraph(root, node =>
            {
                if (!(node.Entry.Entity is IHasTrackingState hasTrackingState))
                {
                    return;
                }

                node.Entry.State = EntityState.Detached;

                // Get related parent entity
                if (node.SourceEntry != null)
                {
                    var relationship = node.InboundNavigation?.GetRelationshipType();
                    switch (relationship)
                    {
                    case RelationshipType.OneToOne:
                        if (node.SourceEntry.State == EntityState.Added)
                        {
                            node.Entry.State = TrackingState.Added.ToEntityState();
                        }
                        else if (node.SourceEntry.State == EntityState.Deleted)
                        {
                            node.Entry.State = TrackingState.Deleted.ToEntityState();
                        }
                        else
                        {
                            node.Entry.State = hasTrackingState.TrackingState.ToEntityState();
                        }

                        return;

                    case RelationshipType.ManyToOne:
                        if (node.SourceEntry.State == EntityState.Added)
                        {
                            node.Entry.State = TrackingState.Added.ToEntityState();
                            return;
                        }

                        var parent = node.SourceEntry.Entity as IHasTrackingState;
                        if (node.SourceEntry.State == EntityState.Deleted ||
                            parent?.TrackingState == TrackingState.Deleted)
                        {
                            try
                            {
                                node.Entry.State = TrackingState.Deleted.ToEntityState();
                            }
                            catch (InvalidOperationException e)
                            {
                                throw new InvalidOperationException(
                                    @"An entity may not be marked as Deleted if it has related entities which are marked as Added.
                                            Remove added related entities before deleting a parent entity.",
                                    e);
                            }

                            return;
                        }

                        break;

                    case RelationshipType.OneToMany:
                        // If ITrackedEntity is set deleted set entity state to unchanged,
                        // since it may be related to other entities.
                        if (hasTrackingState.TrackingState == TrackingState.Deleted)
                        {
                            node.Entry.State = TrackingState.Unchanged.ToEntityState();
                            return;
                        }

                        break;
                    }
                }

                node.Entry.State = hasTrackingState.TrackingState.ToEntityState();
            });
 /// <summary>
 /// Set entity state to Detached for entities in an object graph.
 /// </summary>
 /// <param name="context">Used to query and save changes to a database</param>
 /// <param name="item">Object that implements IHasTrackingState</param>
 public static void DetachEntities(this DbContext context, IHasTrackingState item)
 {
     context.TraverseGraph(item, n => n.Entry.State = EntityState.Detached);
 }