public async Task RebuildNodesAsync(IEnumerable <Benchmark> benchmarks)
        {
            BufferBlock <Benchmark> buffer = new BufferBlock <Benchmark>();

#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
            Task.Run(() => RunDiscovery(benchmarks, buffer));
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed

            _treeViewModel.IsLoading = true;
            _treeViewModel.IsEmpty   = false;
            _treeViewModel.Nodes     = new ObservableCollection <BenchmarkTreeNode>();

            ProjectBenchmarkTreeNode lastProjectNode = null;
            while (await buffer.OutputAvailableAsync())
            {
                Benchmark currentBenchmark = await buffer.ReceiveAsync();

                _treeViewModel.IsLoading = false;

                var projectNode = (ProjectBenchmarkTreeNode)GetOrInsertNode(_treeViewModel.Nodes, currentBenchmark.ProjectName, () => CreateProjectNode(currentBenchmark));
                if (lastProjectNode != null && projectNode != lastProjectNode)
                {
                    lastProjectNode.IsLoading = false;
                }

                BuildHierarchy(_treeViewModel.Nodes, projectNode, currentBenchmark);
                lastProjectNode = projectNode;
            }
            if (lastProjectNode != null)
            {
                lastProjectNode.IsLoading = false;
            }
            _treeViewModel.IsLoading = false;

            if (_treeViewModel.Nodes.Count == 0)
            {
                _treeViewModel.IsEmpty = true;
            }
        }
        private void BuildNamespaceClassHierarchy(ObservableCollection <BenchmarkTreeNode> nodeList, ProjectBenchmarkTreeNode projectNode, Benchmark benchmark)
        {
            BenchmarkTreeNode lastNamespaceNode = projectNode;

            string[] namespaceParts = benchmark.Namespace.Split('.');
            foreach (var namespacePart in namespaceParts)
            {
                lastNamespaceNode = GetOrInsertNode(lastNamespaceNode, namespacePart, p => CreateNamespaceNode(p, namespacePart));
            }

            var classNode = GetOrInsertNode(lastNamespaceNode, benchmark.ClassName, p => CreateClassNode(p, benchmark));

            GetOrInsertNode(classNode, benchmark.MethodName, p => CreateMethodNode(p, benchmark));
        }
        private void BuildProjectClassHierarchy(ObservableCollection <BenchmarkTreeNode> nodeList, ProjectBenchmarkTreeNode projectNode, Benchmark benchmark)
        {
            var classNode = GetOrInsertNode(projectNode, benchmark.ClassName, p => CreateClassNode(p, benchmark));

            GetOrInsertNode(classNode, benchmark.MethodName, p => CreateMethodNode(p, benchmark));
        }
        private void BuildHierarchy(ObservableCollection <BenchmarkTreeNode> nodeList, ProjectBenchmarkTreeNode projectNode, Benchmark benchmark)
        {
            switch (_grouping)
            {
            case Grouping.ProjectClass:
                BuildProjectClassHierarchy(nodeList, projectNode, benchmark);
                break;

            case Grouping.ProjectNamespaceClass:
                BuildNamespaceClassHierarchy(nodeList, projectNode, benchmark);
                break;

            case Grouping.ProjectCategoryClass:
                BuildCategoryClassHierarchy(nodeList, projectNode, benchmark);
                break;

            default:
                throw new ArgumentOutOfRangeException();
            }
        }
 private void BuildCategoryClassHierarchy(ObservableCollection <BenchmarkTreeNode> nodeList, ProjectBenchmarkTreeNode projectNode, Benchmark benchmark)
 {
     foreach (string category in benchmark.Categories)
     {
         var categoryNode = GetOrInsertNode(projectNode, category, p => CreateCategoryNode(p, category));
         var classNode    = GetOrInsertNode(categoryNode, benchmark.ClassName, p => CreateClassNode(p, benchmark));
         GetOrInsertNode(classNode, benchmark.MethodName, p => CreateMethodNode(p, benchmark));
     }
 }