private async Task AnalyzeAndPersistAsync(string productName, string version, IEnumerable <DbFeature> features, IEnumerable <AnalysisReport> reports) { // Collect the Wheresteps of the feature for easy access var featureStepMaps = from feature in features select new { Feature = feature, WhereSteps = feature.Scenarios.SelectMany(scenario => scenario.Steps.Where(step => step.StepKeyword == StepKeyword.When).Select(step => step.Content)).ToList() }; var activeInvocations = new List <DbInvocation>(); // Remove any old invocations in these features foreach (var feature in features) { feature.DirectInvocationSignatures = new List <string>(); } var invocationsToPostProcess = new List <(DbInvocation dbInvocation, Invocation invocationToFlatten)>(); // Determine the possible entrypoints foreach (var invocation in reports.SelectMany(report => report.RootInvocations).Where(ri => ri.RegularExpressions?.Length > 0)) { // Collect the features which perform this invocation var invokingFeatures = new List <DbFeature>(); foreach (var expression in invocation.RegularExpressions) { invokingFeatures.AddRange( featureStepMaps.Where(map => map.WhereSteps.Any(step => Regex.IsMatch(step, expression))) .Select(map => map.Feature)); } // Only process this invocation if it is actually used if (invokingFeatures.Any()) { // Update the features var localInvocations = GetHighestLocalInvocations(invocation).ToList(); var localInvocationSignatures = localInvocations.SelectMany(i => { List <string> invocations = new List <string>(); invocations.Add(i.Signature); // Add the interface definitions, if available if (i.InterfaceDefinitions != null) { invocations.AddRange(i.InterfaceDefinitions); } return(invocations); }).ToList(); invokingFeatures.ForEach(feature => feature.DirectInvocationSignatures.AddRange(localInvocationSignatures)); // Prepare the invocation for storage activeInvocations.AddRange(localInvocations.SelectMany(i => { List <DbInvocation> dbInvocations = new List <DbInvocation>(); // Add the current invocation var dbInvocation = new DbInvocation() { Signature = i.Signature }; dbInvocations.Add(dbInvocation); invocationsToPostProcess.Add((dbInvocation, i)); // Add a copy for each interface definition if (i.InterfaceDefinitions != null) { var dbis = i.InterfaceDefinitions.Select(definition => new DbInvocation() { Signature = definition } ).ToList(); dbInvocations.AddRange(dbis); dbis.ForEach(dbi => invocationsToPostProcess.Add((dbi, i))); } return(dbInvocations); })); } } var strippedDbInvocations = activeInvocations.Select(ai => ai.Signature).ToList(); // Flatten the invocations foreach (var invocation in invocationsToPostProcess) { invocation.dbInvocation.InvokedSignatures = FlattenUntilAMatchIsFound(invocation.invocationToFlatten, strippedDbInvocations); } // Make sure we only save unique signatures foreach (var feature in features) { feature.DirectInvocationSignatures = new List <string>(feature.DirectInvocationSignatures.Distinct()); } // Persist the features await _featureManager.PersistDbFeatures(features); // Persist the activeInvocations await _analysisReportManager.PersistDbInvocationsAsync(productName, version, activeInvocations.GroupBy(i => i.Signature).Select(g => g.First())); }