/// <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(); } } }); }
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); }