/// <summary> /// Returns an unconfigured project level contexts for given project file path. /// </summary> /// <param name="projectFilePath">Full path to project path.</param> /// <returns> /// Instance of <see cref="IDependenciesGraphProjectContext"/> or null if context was not found for given project file. /// </returns> public IDependenciesGraphProjectContext GetProjectContext(string projectFilePath) { if (string.IsNullOrEmpty(projectFilePath)) { throw new ArgumentException(nameof(projectFilePath)); } IDependenciesGraphProjectContext context = null; if (ProjectContexts.TryGetValue(projectFilePath, out context)) { return(context); } context = ProjectExportProvider.GetExport <IDependenciesGraphProjectContext>(projectFilePath); if (context == null) { return(null); } ProjectContexts[projectFilePath] = context; context.ProjectContextChanged += OnProjectContextChanged; context.ProjectContextUnloaded += OnProjectContextUnloaded; return(context); }
private async Task TrackChangesOnGraphContextAsync(IGraphContext graphContext, IDependenciesGraphProjectContext updatedProjectContext) { foreach (var inputGraphNode in graphContext.InputNodes) { var projectPath = GetPartialValueFromGraphNodeId(inputGraphNode.Id, CodeGraphNodeIdName.Assembly); if (string.IsNullOrEmpty(projectPath) || !projectPath.Equals(updatedProjectContext.ProjectFilePath, StringComparison.OrdinalIgnoreCase)) { continue; } var existingNodeInfo = inputGraphNode.GetValue <IDependencyNode>( DependenciesGraphSchema.DependencyNodeProperty); if (existingNodeInfo == null) { continue; } var subTreeProvider = await GetSubTreeProviderAsync(graphContext, inputGraphNode, projectPath, existingNodeInfo.Id).ConfigureAwait(false); if (subTreeProvider == null) { continue; } // Get updated reference from the new snapshot var updatedNodeInfo = subTreeProvider.GetDependencyNode(existingNodeInfo.Id); if (updatedNodeInfo == null) { continue; } using (var scope = new GraphTransactionScope()) { // Diff existing node children and updated node children to get whats removed var nodesToRemove = existingNodeInfo.Children.Except(updatedNodeInfo.Children); foreach (var nodeToRemove in nodesToRemove) { RemoveGraphNode(graphContext, projectPath, nodeToRemove); } // Diff updated node children and existing node children to get whats added var nodesToAdd = updatedNodeInfo.Children.Except(existingNodeInfo.Children); foreach (var nodeToAdd in nodesToAdd) { AddGraphNode(graphContext, projectPath, subTreeProvider, inputGraphNode, nodeToAdd); } // Update the node info saved on the 'inputNode' inputGraphNode.SetValue(DependenciesGraphSchema.DependencyNodeProperty, updatedNodeInfo); scope.Complete(); } } }
/// <summary> /// Property ExpandedGraphContexts remembers graph expanded so far. /// Each context represents one level in the graph, i.e. a node and its first level dependencies /// Tracking changes over all expanded contexts ensures that all levels are processed /// and updated when there are any changes in nodes data. /// </summary> internal async Task TrackChangesAsync(IDependenciesGraphProjectContext updatedProjectContext) { foreach (var graphContext in ExpandedGraphContexts.ToList()) { try { await TrackChangesOnGraphContextAsync(graphContext, updatedProjectContext).ConfigureAwait(false); } finally { // Calling OnCompleted ensures that the changes are reflected in UI graphContext.OnCompleted(); } } }
public ProjectContextEventArgs(IDependenciesGraphProjectContext context) { Context = context; }
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(); } } }
public ProjectContextEventArgs(IDependenciesGraphProjectContext context, IDependenciesChangeDiff diff = null) { Context = context; Diff = diff ?? new DependenciesChange().GetDiff(); }