/// <summary> /// Discovers if there any changes to apply for existing node, after it's context changed. /// </summary> private bool AnyChangesToTrack(IDependencyNode node, IDependencyNode updatedNode, IDependenciesChangeDiff diff, out IEnumerable <IDependencyNode> nodesToAdd, out IEnumerable <IDependencyNode> nodesToRemove) { var existingChildren = node.Children; if (node.Flags.Contains(DependencyNode.DependsOnOtherProviders)) { var remove = new HashSet <IDependencyNode>(diff.RemovedNodes); var add = new HashSet <IDependencyNode>(diff.AddedNodes); foreach (var changedNode in diff.UpdatedNodes) { remove.Add(changedNode); add.Add(changedNode); } nodesToAdd = add; nodesToRemove = remove; } else { var updatedChildren = updatedNode.Children; var comparer = new DependencyNodeResolvedStateComparer(); nodesToRemove = existingChildren.Except(updatedChildren, comparer).ToList(); nodesToAdd = updatedChildren.Except(existingChildren, comparer).ToList(); } return(nodesToAdd.Any() || nodesToRemove.Any()); }
private async Task TrackChangesOnGraphContextAsync(IGraphContext graphContext, IDependenciesGraphProjectContext updatedProjectContext) { foreach (var inputGraphNode in graphContext.InputNodes.ToList()) { var existingNodeInfo = inputGraphNode.GetValue <IDependencyNode>( DependenciesGraphSchema.DependencyNodeProperty); if (existingNodeInfo == null) { continue; } var projectPath = GetPartialValueFromGraphNodeId(inputGraphNode.Id, CodeGraphNodeIdName.Assembly); bool shouldProcess = !string.IsNullOrEmpty(projectPath) && projectPath.Equals(updatedProjectContext.ProjectFilePath, StringComparison.OrdinalIgnoreCase); var contextProject = updatedProjectContext.ProjectFilePath; if (!shouldProcess) { shouldProcess = !string.IsNullOrEmpty(existingNodeInfo.Id.ContextProject) && existingNodeInfo.Id.ContextProject.Equals(updatedProjectContext.ProjectFilePath, StringComparison.OrdinalIgnoreCase); } if (!shouldProcess) { continue; } var subTreeProvider = await GetSubTreeProviderAsync(graphContext, inputGraphNode, projectPath, existingNodeInfo.Id).ConfigureAwait(false); if (subTreeProvider == null) { continue; } // store existing children, since existingNodeInfo instance might be updated // (this is a side effect for top level nodes) var existingChildren = new HashSet <IDependencyNode>(existingNodeInfo.Children); // Get updated reference from the new snapshot var updatedNodeInfo = subTreeProvider.GetDependencyNode(existingNodeInfo.Id); if (updatedNodeInfo == null) { continue; } using (var scope = new GraphTransactionScope()) { var comparer = new DependencyNodeResolvedStateComparer(); // Diff existing node children and updated node children to get whats removed var nodesToRemove = existingChildren.Except(updatedNodeInfo.Children, comparer).ToList(); // Diff updated node children and existing node children to get whats added var nodesToAdd = updatedNodeInfo.Children.Except(existingChildren, comparer).ToList(); foreach (var nodeToRemove in nodesToRemove) { RemoveGraphNode(graphContext, contextProject, nodeToRemove); existingNodeInfo.RemoveChild(nodeToRemove); } foreach (var nodeToAdd in nodesToAdd) { AddGraphNode(graphContext, contextProject, null, inputGraphNode, nodeToAdd); existingNodeInfo.AddChild(nodeToAdd); } // Update the node info saved on the 'inputNode' inputGraphNode.SetValue(DependenciesGraphSchema.DependencyNodeProperty, updatedNodeInfo); scope.Complete(); } } }