/// <summary> /// Traverse an entity graph asynchronously executing a callback on each node. /// </summary> /// <param name="context">The entity graph context.</param> /// <param name="entity">The entity to start traversing the graph from.</param> /// <param name="callback">The asynchronous callback executed on each node in the entity graph.</param> /// <param name="cancellationToken">A System.Threading.CancellationToken to observe while waiting for the task to complete.</param> public static Task TraverseGraphAsync(this DbContext context, object entity, Func <EntityEntryGraphNode, CancellationToken, Task> callback, CancellationToken cancellationToken = default) { if (context == null) { throw new ArgumentNullException(nameof(context)); } if (callback == null) { throw new ArgumentNullException(nameof(callback)); } #pragma warning disable EF1001 // Internal EF Core API usage. var graph = new EntityEntryGraphIterator( ); var visited = new HashSet <object> ( ); var entry = context.Entry(entity).GetInfrastructure( ); var root = new EntityEntryGraphNode <object?> (entry, null, null, null); return(graph.TraverseGraphAsync(root, async(node, cancellationToken) => { if (!visited.Add(node.Entry.Entity)) { return false; } await callback(node, cancellationToken).ConfigureAwait(false); return true; }, cancellationToken)); #pragma warning restore EF1001 // Internal EF Core API usage. }
/// <summary> /// Traverse an object graph asynchronously executing a callback on each node. /// </summary> /// <param name="context">Used to query and save changes to a database</param> /// <param name="item">Object that implements ITrackable</param> /// <param name="callback">Async callback executed on each node in the object graph</param> public static async Task TraverseGraphAsync(this DbContext context, object item, Func <EntityEntryGraphNode, Task> callback) { #pragma warning disable EF1001 // Internal EF Core API usage. IStateManager stateManager = context.Entry(item).GetInfrastructure().StateManager; var node = new EntityEntryGraphNode <object>(stateManager.GetOrCreateEntry(item), null, null, null); IEntityEntryGraphIterator graphIterator = new EntityEntryGraphIterator(); #pragma warning restore EF1001 // Internal EF Core API usage. var visited = new HashSet <int>(); await graphIterator.TraverseGraphAsync <object>(node, async (n, ct) => { // Check visited if (visited.Contains(n.Entry.Entity.GetHashCode())) { return(false); } // Execute callback await callback(n); // Add visited visited.Add(n.Entry.Entity.GetHashCode()); // Continue traversal return(true); }); }
/// <summary> /// Traverse an object graph executing a callback on each node. /// </summary> /// <param name="context">Used to query and save changes to a database</param> /// <param name="item">Object that implements ITrackable</param> /// <param name="callback">Callback executed on each node in the object graph</param> public static void TraverseGraph(this DbContext context, object item, Action <EntityEntryGraphNode> callback) { IStateManager stateManager = context.Entry(item).GetInfrastructure().StateManager; var node = new EntityEntryGraphNode(stateManager.GetOrCreateEntry(item), null, null); IEntityEntryGraphIterator graphIterator = new EntityEntryGraphIterator(); var visited = new HashSet <int>(); graphIterator.TraverseGraph <object>(node, null, (n, s) => { // Check visited if (visited.Contains(n.Entry.Entity.GetHashCode())) { return(false); } // Execute callback callback(n); // Add visited visited.Add(n.Entry.Entity.GetHashCode()); // Continue traversal return(true); }); }
public virtual void TraverseGraph(EntityEntryGraphNode node, Func<EntityEntryGraphNode, bool> handleNode) { if (!handleNode(node)) { return; } var internalEntityEntry = node.GetInfrastructure(); var navigations = internalEntityEntry.EntityType.GetNavigations(); var stateManager = internalEntityEntry.StateManager; foreach (var navigation in navigations) { var navigationValue = internalEntityEntry[navigation]; if (navigationValue != null) { if (navigation.IsCollection()) { foreach (var relatedEntity in (IEnumerable)navigationValue) { TraverseGraph( node.CreateNode(node, stateManager.GetOrCreateEntry(relatedEntity), navigation), handleNode); } } else { TraverseGraph( node.CreateNode(node, stateManager.GetOrCreateEntry(navigationValue), navigation), handleNode); } } } }
private void TrackGraph(EntityEntryGraphNode e) { if (!(e.Entry.Entity is IState entityWithState)) throw new Exception($"Entity {e.GetType()} must implement {typeof(IState)}"); e.Entry.State = ConvertState(entityWithState.State); }
/// <summary> /// This API supports the Entity Framework Core infrastructure and is not intended to be used /// directly from your code. This API may change or be removed in future releases. /// </summary> public virtual void TraverseGraph(EntityEntryGraphNode node, Func <EntityEntryGraphNode, bool> handleNode) { if (!handleNode(node)) { return; } var internalEntityEntry = node.GetInfrastructure(); var navigations = internalEntityEntry.EntityType.GetNavigations(); var stateManager = internalEntityEntry.StateManager; foreach (var navigation in navigations) { var navigationValue = internalEntityEntry[navigation]; if (navigationValue != null) { if (navigation.IsCollection()) { foreach (var relatedEntity in (IEnumerable)navigationValue) { TraverseGraph( node.CreateNode(node, stateManager.GetOrCreateEntry(relatedEntity), navigation), handleNode); } } else { TraverseGraph( node.CreateNode(node, stateManager.GetOrCreateEntry(navigationValue), navigation), handleNode); } } } }
/// <summary> /// Traverse an object graph asynchronously executing a callback on each node. /// Depends on Entity Framework Core infrastructure, which may change in future releases. /// </summary> /// <param name="context">Used to query and save changes to a database</param> /// <param name="item">Object that implements ITrackable</param> /// <param name="callback">Async callback executed on each node in the object graph</param> public static async Task TraverseGraphAsync(this DbContext context, object item, Func <EntityEntryGraphNode, Task> callback) { IStateManager stateManager = context.Entry(item).GetInfrastructure().StateManager; var node = new EntityEntryGraphNode(stateManager.GetOrCreateEntry(item), null, null); IEntityEntryGraphIterator graphIterator = new EntityEntryGraphIterator(); var visited = new HashSet <int>(); await graphIterator.TraverseGraphAsync(node, async (n, ct) => { // Check visited if (visited.Contains(n.Entry.Entity.GetHashCode())) { return(false); } // Execute callback await callback(n); // Add visited visited.Add(n.Entry.Entity.GetHashCode()); // Return true if node state is null return(true); }); }
/// <summary> /// Traverse an object graph executing a callback on each node. /// </summary> /// <param name="context">Used to query and save changes to a database</param> /// <param name="item">Object that implements ITrackable</param> /// <param name="callback">Callback executed on each node in the object graph</param> public static void TraverseGraph(this DbContext context, object item, Action <EntityEntryGraphNode> callback) { #pragma warning disable EF1001 // Internal EF Core API usage. IStateManager stateManager = context.Entry(item).GetInfrastructure().StateManager; var node = new EntityEntryGraphNode <object>(stateManager.GetOrCreateEntry(item), null, null, null); IEntityEntryGraphIterator graphIterator = new EntityEntryGraphIterator(); #pragma warning restore EF1001 // Internal EF Core API usage. var visited = new HashSet <object>(); graphIterator.TraverseGraph(node, n => { // Check visited if (visited.Contains(n.Entry.Entity)) { return(false); } // Execute callback callback(n); // Add visited visited.Add(n.Entry.Entity); // Continue traversal return(true); }); }
protected void TrackGraph(EntityEntryGraphNode obj) { var navAttr = obj.InboundNavigation?.PropertyInfo?.GetCustomAttribute <NotNaviguableAttribute>(); if (CannotNaviguate(navAttr)) { obj.Entry.State = EntityState.Unchanged; return; } if (obj.Entry.Entity is BasePersistableEntity baseEntity) { baseEntity.EditDate = DateTime.Now; } if (obj.Entry.IsKeySet) { if (_createMode || obj.Entry.GetDatabaseValues() == null) { obj.Entry.State = EntityState.Added; } else { obj.Entry.State = EntityState.Modified; } } else { obj.Entry.State = EntityState.Added; } }
/// <summary> /// Traverse an entity graph executing a callback on each node. /// </summary> /// <param name="context">The entity graph context.</param> /// <param name="entity">The entity to start traversing the graph from.</param> /// <param name="callback">The callback executed on each node in the entity graph.</param> public static void TraverseGraph(this DbContext context, object entity, Action <EntityEntryGraphNode> callback) { if (context == null) { throw new ArgumentNullException(nameof(context)); } if (callback == null) { throw new ArgumentNullException(nameof(callback)); } #pragma warning disable EF1001 // Internal EF Core API usage. var graph = new EntityEntryGraphIterator( ); var visited = new HashSet <object> ( ); var entry = context.Entry(entity).GetInfrastructure( ); var root = new EntityEntryGraphNode <object?> (entry, null, null, null); graph.TraverseGraph(root, node => { if (!visited.Add(node.Entry.Entity)) { return(false); } callback(node); return(true); }); #pragma warning restore EF1001 // Internal EF Core API usage. }
private void UpdateStateOfItems(EntityEntryGraphNode node) { node.Entry.State = EntityState.Modified; if (node.Entry.Entity.GetType() == typeof(ChangeRequestTask)) { node.Entry.State = EntityState.Unchanged; } }
/// <summary> /// This API supports the Entity Framework Core infrastructure and is not intended to be used /// directly from your code. This API may change or be removed in future releases. /// </summary> public virtual async Task TraverseGraphAsync <TState>( EntityEntryGraphNode node, TState state, Func <EntityEntryGraphNode, TState, CancellationToken, Task <bool> > handleNode, CancellationToken cancellationToken = default) { if (!await handleNode(node, state, cancellationToken)) { return; } var internalEntityEntry = node.GetInfrastructure(); var navigations = internalEntityEntry.EntityType.GetNavigations(); var stateManager = internalEntityEntry.StateManager; foreach (var navigation in navigations) { var navigationValue = internalEntityEntry[navigation]; if (navigationValue != null) { var targetType = navigation.GetTargetType(); if (navigation.IsCollection()) { foreach (var relatedEntity in ((IEnumerable)navigationValue).Cast <object>().ToList()) { var targetEntry = targetType.HasDefiningNavigation() ? stateManager.GetOrCreateEntry(relatedEntity, targetType) : stateManager.GetOrCreateEntry(relatedEntity); await TraverseGraphAsync( node.CreateNode(node, targetEntry, navigation), state, handleNode, cancellationToken); } } else { var targetEntry = targetType.HasDefiningNavigation() ? stateManager.GetOrCreateEntry(navigationValue, targetType) : stateManager.GetOrCreateEntry(navigationValue); await TraverseGraphAsync( node.CreateNode(node, targetEntry, navigation), state, handleNode, cancellationToken); } } } }
public void Target(EntityEntryGraphNode n) { n.Entry.State = EntityState.Detached; var entity = (IState)n.Entry.Entity; n.Entry.State = entity.State == State.Added ? EntityState.Added : entity.State == State.Modified ? EntityState.Modified : entity.State == State.Deleted ? EntityState.Deleted : EntityState.Unchanged; int x = 1; }
private static bool PaintAction(EntityEntryGraphNode node) { var internalEntityEntry = node.GetInfrastructure(); if (internalEntityEntry.EntityState != EntityState.Detached) { return(false); } internalEntityEntry.SetEntityState( internalEntityEntry.IsKeySet ? (EntityState)node.NodeState : EntityState.Added, acceptChanges: true); return(true); }
/// <summary> /// Creates a new node for the entity that is being traversed next in the graph. /// </summary> /// <param name="currentNode">The node that the entity is being traversed from.</param> /// <param name="internalEntityEntry"> /// The internal entry tracking information about the entity being traversed to. /// </param> /// <param name="reachedVia">The navigation property that is being traversed to reach the new node.</param> /// <returns>The newly created node.</returns> public override EntityEntryGraphNode CreateNode( EntityEntryGraphNode currentNode, InternalEntityEntry internalEntityEntry, INavigationBase reachedVia) { Check.NotNull(currentNode, nameof(currentNode)); Check.NotNull(internalEntityEntry, nameof(internalEntityEntry)); Check.NotNull(reachedVia, nameof(reachedVia)); return(new EntityEntryGraphNode <TState>( internalEntityEntry, ((EntityEntryGraphNode <TState>)currentNode).NodeState, currentNode.Entry.GetInfrastructure(), reachedVia)); }
private void UpdateNodeRecursively(DbContext context, EntityEntryGraphNode node) { EntityEntry current = node.Entry; EntityEntry fatherNode = node.SourceEntry; entitiesThatShouldBeInUpdate.Add(HelperFunctions <Entity> .GetKeys(current)); if (EntryExistsInChangeTracker(context, current)) // Entity is already being tracked in a different node so the current context cant track it { EnqueueFatherNodeToLeftToUpdateQueue(fatherNode); // Entity will be updated in a new Conxtext in the future since it cant be tracked in the current context } else { SetEntityAsModifiedOrAdded(context, current, node); } }
public void UpperBoundTrackGraph <T>(EntityEntryGraphNode node, string memberName, Func <T> boundMemberCreator, string keyPostfix = null) { keyPostfix = keyPostfix ?? "Id"; var property = node.Entry.Property(memberName); if ((Guid)node.Entry.Property($"{memberName}{keyPostfix}").CurrentValue != default(Guid)) { property.CurrentValue = property.EntityEntry.GetDatabaseValues(); var newnode = new EntityEntryGraphNode(node.Entry.GetInfrastructure(), property.EntityEntry.GetInfrastructure(), node.InboundNavigation); UpperBoundTrackGraph <T>(newnode, memberName, boundMemberCreator, keyPostfix); } else { node.Entry.Property("Parent").CurrentValue = boundMemberCreator(); } }
private static async Task <bool> PaintActionAsync(EntityEntryGraphNode node, CancellationToken cancellationToken) { var internalEntityEntry = node.GetInfrastructure(); if (internalEntityEntry.EntityState != EntityState.Detached) { return(false); } await internalEntityEntry.SetEntityStateAsync( internalEntityEntry.IsKeySet?(EntityState)node.NodeState : EntityState.Added, acceptChanges : true, cancellationToken : cancellationToken); return(true); }
/// <summary> /// This API supports the Entity Framework Core infrastructure and is not intended to be used /// directly from your code. This API may change or be removed in future releases. /// </summary> public virtual void TraverseGraph <TState>( EntityEntryGraphNode node, TState state, Func <EntityEntryGraphNode, TState, bool> handleNode) { if (!handleNode(node, state)) { return; } var internalEntityEntry = node.GetInfrastructure(); var navigations = ((EntityType)internalEntityEntry.EntityType).GetNavigations(); var stateManager = internalEntityEntry.StateManager; foreach (var navigation in navigations) { var navigationValue = internalEntityEntry[navigation]; if (navigationValue != null) { var targetEntityType = navigation.GetTargetType(); if (navigation.IsCollection()) { foreach (var relatedEntity in ((IEnumerable)navigationValue).Cast <object>().ToList()) { var targetEntry = targetEntityType.HasDefiningNavigation() ? stateManager.GetOrCreateEntry(relatedEntity, targetEntityType) : stateManager.GetOrCreateEntry(relatedEntity); TraverseGraph( node.CreateNode(node, targetEntry, navigation), state, handleNode); } } else { var targetEntry = targetEntityType.HasDefiningNavigation() ? stateManager.GetOrCreateEntry(navigationValue, targetEntityType) : stateManager.GetOrCreateEntry(navigationValue); TraverseGraph( node.CreateNode(node, targetEntry, navigation), state, handleNode); } } } }
/// <summary> /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to /// the same compatibility standards as public APIs. It may be changed or removed without notice in /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// </summary> public virtual async Task TraverseGraphAsync <TState>( EntityEntryGraphNode <TState> node, Func <EntityEntryGraphNode <TState>, CancellationToken, Task <bool> > handleNode, CancellationToken cancellationToken = default) { if (!await handleNode(node, cancellationToken).ConfigureAwait(false)) { return; } var internalEntityEntry = node.GetInfrastructure(); var navigations = internalEntityEntry.EntityType.GetNavigations() .Concat <INavigationBase>(internalEntityEntry.EntityType.GetSkipNavigations()); var stateManager = internalEntityEntry.StateManager; foreach (var navigation in navigations) { var navigationValue = internalEntityEntry[navigation]; if (navigationValue != null) { var targetType = navigation.TargetEntityType; if (navigation.IsCollection) { foreach (var relatedEntity in ((IEnumerable)navigationValue).Cast <object>().ToList()) { var targetEntry = stateManager.GetOrCreateEntry(relatedEntity, targetType); await TraverseGraphAsync( (EntityEntryGraphNode <TState>) node.CreateNode(node, targetEntry, navigation), handleNode, cancellationToken) .ConfigureAwait(false); } } else { var targetEntry = stateManager.GetOrCreateEntry(navigationValue, targetType); await TraverseGraphAsync( (EntityEntryGraphNode <TState>) node.CreateNode(node, targetEntry, navigation), handleNode, cancellationToken) .ConfigureAwait(false); } } } }
private static bool PaintAction(EntityEntryGraphNode node) { var internalEntityEntry = node.GetInfrastructure(); if (internalEntityEntry.EntityState != EntityState.Detached) { return false; } if (node.InboundNavigation != null && !internalEntityEntry.IsKeySet) { node.NodeState = EntityState.Added; } internalEntityEntry.SetEntityState((EntityState)node.NodeState, acceptChanges: true); return true; }
private static bool PaintAction(EntityEntryGraphNode n) { var internalEntityEntry = n.GetInfrastructure(); if (internalEntityEntry.EntityState != EntityState.Detached || (n.InboundNavigation != null && n.InboundNavigation.IsDependentToPrincipal())) { return false; } if (!internalEntityEntry.IsKeySet) { n.NodeState = EntityState.Added; } internalEntityEntry.SetEntityState((EntityState)n.NodeState, acceptChanges: true); return true; }
private static bool PaintAction(EntityEntryGraphNode n) { var internalEntityEntry = n.GetInfrastructure(); if (internalEntityEntry.EntityState != EntityState.Detached || (n.InboundNavigation != null && n.InboundNavigation.IsDependentToPrincipal())) { return(false); } if (!internalEntityEntry.IsKeySet) { n.NodeState = EntityState.Added; } internalEntityEntry.SetEntityState((EntityState)n.NodeState, acceptChanges: true); return(true); }
private static bool PaintAction(EntityEntryGraphNode node) { var internalEntityEntry = node.GetInfrastructure(); if (internalEntityEntry.EntityState != EntityState.Detached) { return(false); } if (node.InboundNavigation != null && !internalEntityEntry.IsKeySet) { node.NodeState = EntityState.Added; } internalEntityEntry.SetEntityState((EntityState)node.NodeState, acceptChanges: true); return(true); }
/// <summary> /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to /// the same compatibility standards as public APIs. It may be changed or removed without notice in /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// </summary> public virtual void TraverseGraph <TState>( EntityEntryGraphNode <TState> node, Func <EntityEntryGraphNode <TState>, bool> handleNode) { if (!handleNode(node)) { return; } var internalEntityEntry = node.GetInfrastructure(); var navigations = internalEntityEntry.EntityType.GetNavigations() .Concat <INavigationBase>(internalEntityEntry.EntityType.GetSkipNavigations()); var stateManager = internalEntityEntry.StateManager; foreach (var navigation in navigations) { var navigationValue = internalEntityEntry[navigation]; if (navigationValue != null) { var targetEntityType = navigation.TargetEntityType; if (navigation.IsCollection) { foreach (var relatedEntity in ((IEnumerable)navigationValue).Cast <object>().ToList()) { var targetEntry = stateManager.GetOrCreateEntry(relatedEntity, targetEntityType); TraverseGraph( (EntityEntryGraphNode <TState>)node.CreateNode(node, targetEntry, navigation), handleNode); } } else { var targetEntry = stateManager.GetOrCreateEntry(navigationValue, targetEntityType); TraverseGraph( (EntityEntryGraphNode <TState>)node.CreateNode(node, targetEntry, navigation), handleNode); } } } }
private void ConvertStateOfNode(EntityEntryGraphNode node) { IState entity = (IState)node.Entry.Entity; node.Entry.State = ConvertToEFState(entity.ObjectState); }
public virtual void TrackEntity(EntityEntryGraphNode node) => node.Entry.GetInfrastructure().SetEntityState(DetermineState(node.Entry), acceptChanges: true);
/// <summary> /// Converts the state of an object that self tracks changes to an the appropriate state for Entity Framework /// </summary> /// <remarks> /// This code is from the Julie Lerman article in MSDN Magazine at /// https://msdn.microsoft.com/magazine/mt767693 /// </remarks> /// <param name="node"></param> public static void ConvertStateOfNode(EntityEntryGraphNode node) { IObjectState entity = (IObjectState)node.Entry.Entity; node.Entry.State = ConvertToEFState(entity.ObjectState); }
private void SetEntityAsModifiedOrAdded(DbContext context, EntityEntry entry, EntityEntryGraphNode node) { if (EntryExistsInDb(context, entry)) { entry.State = EntityState.Modified; } else { entry.State = EntityState.Added; } }
public static void TrackGraph_CallbackAdded(EntityEntryGraphNode e) { Console.WriteLine("TrackGraph_Callback: " + e.Entry.Entity + ": " + e.NodeState + ", " + e.Entry.State); e.Entry.State = EntityState.Added; Console.WriteLine("TrackGraph_Callback: " + e.Entry.Entity + ": " + e.NodeState + ", " + e.Entry.State); }
/// <summary> /// To be used with EfCore ChangeTracker.TrackGraph. /// Convert the PersistChange of the PersistEntity to the EfCore state. /// </summary> /// <param name="node"><see cref="EntityEntryGraphNode"/></param> public static void Tracker(EntityEntryGraphNode node) { node.Entry.State = node.Entry.Entity is IPersistEntity persistEntity?persistEntity.PersistChange.Convert() : EntityState.Unchanged; }