/// <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 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 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. }