public sealed override bool TryHandleRequest(IGraphContext graphContext)
        {
            if (!CanHandle(graphContext))
            {
                return false;
            }

            bool trackChanges = false;

            foreach (GraphNode inputGraphNode in graphContext.InputNodes)
            {
                if (graphContext.CancelToken.IsCancellationRequested)
                {
                    return trackChanges;
                }

                string? projectPath = inputGraphNode.Id.GetValue(CodeGraphNodeIdName.Assembly);

                if (string.IsNullOrEmpty(projectPath))
                {
                    continue;
                }

                IDependency? dependency = FindDependency(inputGraphNode, out IDependenciesSnapshot? snapshot);

                if (dependency == null || snapshot == null)
                {
                    continue;
                }

                IDependenciesGraphViewProvider? viewProvider = FindViewProvider(dependency);

                if (viewProvider == null)
                {
                    continue;
                }

                using var scope = new GraphTransactionScope();
                ProcessInputNode(graphContext, inputGraphNode, dependency, snapshot, viewProvider, projectPath, ref trackChanges);

                scope.Complete();
            }

            return trackChanges;
        }
        /// <summary>
        /// ProjectContextChanged gets fired every time dependencies change for projects across solution.
        /// <see cref="_expandedGraphContexts"/> contains all nodes that we need to check for potential updates
        /// in their child dependencies.
        /// </summary>
        private void OnSnapshotChanged(object sender, SnapshotChangedEventArgs e)
        {
            DependenciesSnapshot snapshot = e.Snapshot;

            if (snapshot == null || e.Token.IsCancellationRequested)
            {
                return;
            }

            // _expandedGraphContexts remembers graph expanded or checked 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.
            lock (_lock)
            {
                foreach (IGraphContext graphContext in _expandedGraphContexts)
                {
                    if (e.Token.IsCancellationRequested)
                    {
                        return;
                    }

                    bool anyChanges = false;

                    try
                    {
                        if (HandleChanges(graphContext))
                        {
                            anyChanges = true;
                        }
                    }
                    finally
                    {
                        // Calling OnCompleted ensures that the changes are reflected in UI
                        if (anyChanges)
                        {
                            graphContext.OnCompleted();
                        }
                    }
                }
            }

            return;

            bool HandleChanges(IGraphContext graphContext)
            {
                bool anyChanges = false;

                foreach (GraphNode inputGraphNode in graphContext.InputNodes.ToList())
                {
                    string?existingDependencyId = inputGraphNode.GetValue <string>(DependenciesGraphSchema.DependencyIdProperty);
                    if (string.IsNullOrEmpty(existingDependencyId))
                    {
                        continue;
                    }

                    string?nodeProjectPath = inputGraphNode.Id.GetValue(CodeGraphNodeIdName.Assembly);
                    if (string.IsNullOrEmpty(nodeProjectPath))
                    {
                        continue;
                    }

                    DependenciesSnapshot?updatedSnapshot = _aggregateSnapshotProvider.GetSnapshot(nodeProjectPath !);

                    IDependency?updatedDependency = updatedSnapshot?.FindDependency(existingDependencyId);

                    if (updatedDependency == null) // or updatedSnapshot == null
                    {
                        continue;
                    }

                    IDependenciesGraphViewProvider?viewProvider = _viewProviders
                                                                  .FirstOrDefaultValue((x, d) => x.SupportsDependency(d), updatedDependency);
                    if (viewProvider == null)
                    {
                        continue;
                    }

                    if (!viewProvider.ShouldApplyChanges(nodeProjectPath !, snapshot.ProjectPath, updatedDependency))
                    {
                        continue;
                    }

                    using var scope = new GraphTransactionScope();
                    if (viewProvider.ApplyChanges(
                            graphContext,
                            nodeProjectPath !,
                            updatedDependency,
                            inputGraphNode,
                            updatedSnapshot !.DependenciesByTargetFramework[updatedDependency.TargetFramework]))
                    {
                        anyChanges = true;
                    }

                    scope.Complete();
                }

                return(anyChanges);
            }
        }
        /// <summary>
        /// Generates search graph containing nodes matching search criteria in Solution Explorer
        /// and attaches it to correct top level node.
        /// </summary>
        private void Search(IGraphContext graphContext)
        {
            string searchParametersTypeName            = typeof(ISolutionSearchParameters).GUID.ToString();
            ISolutionSearchParameters searchParameters = graphContext.GetValue <ISolutionSearchParameters>(searchParametersTypeName);

            string?searchTerm = searchParameters?.SearchQuery.SearchString;

            if (searchTerm == null)
            {
                return;
            }

            var cachedDependencyToMatchingResultsMap = new Dictionary <string, HashSet <IDependency> >(StringComparer.OrdinalIgnoreCase);
            var searchResultsPerContext = new Dictionary <string, HashSet <IDependency> >(StringComparer.OrdinalIgnoreCase);

            System.Collections.Generic.IReadOnlyCollection <IDependenciesSnapshot> snapshots = AggregateSnapshotProvider.GetSnapshots();

            foreach (IDependenciesSnapshot snapshot in snapshots)
            {
                searchResultsPerContext[snapshot.ProjectPath] = SearchFlat(
                    searchTerm,
                    snapshot);
            }

            foreach (IDependenciesSnapshot snapshot in snapshots)
            {
                IEnumerable <IDependency> allTopLevelDependencies = snapshot.GetFlatTopLevelDependencies();
                HashSet <IDependency>     matchedDependencies     = searchResultsPerContext[snapshot.ProjectPath];

                using var scope = new GraphTransactionScope();
                foreach (IDependency topLevelDependency in allTopLevelDependencies)
                {
                    ITargetedDependenciesSnapshot targetedSnapshot = snapshot.DependenciesByTargetFramework[topLevelDependency.TargetFramework];

                    if (!cachedDependencyToMatchingResultsMap
                        .TryGetValue(topLevelDependency.Id, out HashSet <IDependency>?topLevelDependencyMatches))
                    {
                        IDependenciesGraphViewProvider?viewProvider = FindViewProvider(topLevelDependency);

                        if (viewProvider == null)
                        {
                            continue;
                        }

                        if (!viewProvider.MatchSearchResults(
                                topLevelDependency,
                                searchResultsPerContext,
                                out topLevelDependencyMatches))
                        {
                            if (matchedDependencies.Count == 0)
                            {
                                continue;
                            }

                            topLevelDependencyMatches = GetMatchingResultsForDependency(
                                topLevelDependency,
                                targetedSnapshot,
                                matchedDependencies,
                                cachedDependencyToMatchingResultsMap);
                        }

                        cachedDependencyToMatchingResultsMap[topLevelDependency.Id] = topLevelDependencyMatches;
                    }

                    if (topLevelDependencyMatches.Count == 0)
                    {
                        continue;
                    }

                    GraphNode topLevelNode = _builder.AddTopLevelGraphNode(graphContext,
                                                                           snapshot.ProjectPath,
                                                                           topLevelDependency.ToViewModel(targetedSnapshot));
                    foreach (IDependency matchedDependency in topLevelDependencyMatches)
                    {
                        GraphNode matchedDependencyNode = _builder.AddGraphNode(graphContext,
                                                                                snapshot.ProjectPath,
                                                                                topLevelNode,
                                                                                matchedDependency.ToViewModel(targetedSnapshot));

                        graphContext.Graph.Links.GetOrCreate(topLevelNode,
                                                             matchedDependencyNode,
                                                             label: null,
                                                             GraphCommonSchema.Contains);
                    }

                    if (topLevelNode != null)
                    {
                        // 'node' is a GraphNode for top level dependency (which is part of solution explorer tree)
                        // Setting ProjectItem category (and correct GraphNodeId) ensures that search graph appears
                        // under right solution explorer hierarchy item
                        topLevelNode.AddCategory(CodeNodeCategories.ProjectItem);
                    }
                }

                scope.Complete();
            }

            graphContext.OnCompleted();
        }