/// <summary> /// Performs filtering of provided template list for --search option. Filters applied: template name filter, --search option filters, template parameters filter. /// Only templates that exactly match the filters are returned. /// </summary> /// <param name="templateInfo">the list of templates to be filtered</param> /// <param name="hostDataLoader">data of the host</param> /// <param name="commandInput">new command data used in CLI</param> /// <returns>filtered list of templates</returns> public static IReadOnlyCollection <ITemplateMatchInfo> PerformCoreTemplateQueryForSearch(IEnumerable <ITemplateInfo> templateInfo, IHostSpecificDataLoader hostDataLoader, INewCommandInput commandInput) { IReadOnlyList <FilterableTemplateInfo> filterableTemplateInfo = SetupFilterableTemplateInfoFromTemplateInfo(templateInfo.ToList()); List <Func <ITemplateInfo, MatchInfo?> > searchFilters = new List <Func <ITemplateInfo, MatchInfo?> >() { WellKnownSearchFilters.NameFilter(commandInput.TemplateName), }; searchFilters.AddRange(SupportedFilterOptions.SupportedSearchFilters .OfType <TemplateFilterOption>() .Select(filter => filter.TemplateMatchFilter(commandInput))); IReadOnlyCollection <ITemplateMatchInfo> matchedTemplates = TemplateListFilter.GetTemplateMatchInfo(filterableTemplateInfo, TemplateListFilter.ExactMatchFilter, searchFilters.ToArray()); AddParameterMatchingToTemplates(matchedTemplates, hostDataLoader, commandInput); return(matchedTemplates.Where(t => t.IsInvokableMatch()).ToList()); }
private static void DisplayPartialNameMatchLanguageAndContextProblems(string templateName, string templateLanguage, string context, TemplateListResolutionResult templateResolutionResult, out bool shouldShowTemplateList) { shouldShowTemplateList = false; if (templateResolutionResult.IsNoTemplatesMatchedState || templateResolutionResult.UsingPartialMatches) { ShowNoTemplatesFoundMessage(templateName, templateLanguage, context); Reporter.Error.WriteLine(); return; } bool anythingReported = false; int partialTemplatesMatchCount = templateResolutionResult.ContextProblemMatchGroups.Count(templateGroup => { // all templates in a group should have the same context & name if (templateGroup[0].Info.Tags != null && templateGroup[0].Info.Tags.TryGetValue("type", out ICacheTag typeTag)) { MatchInfo?matchInfo = WellKnownSearchFilters.ContextFilter(context)(templateGroup[0].Info); return((matchInfo?.Kind ?? MatchKind.Mismatch) == MatchKind.Mismatch); } // this really shouldn't ever happen. But better to have a generic error than quietly ignore the partial match. // Cannot retrieve the type for {0}. Reporter.Error.WriteLine(string.Format(LocalizableStrings.GenericPlaceholderTemplateContextError, templateGroup[0].Info.Name).Bold().Red()); anythingReported = true; return(false); }); if (partialTemplatesMatchCount > 0) { ShowNoTemplatesFoundMessage(templateName, templateLanguage, context); // {0} template(s) partially matched, but failed on {1}. Reporter.Error.WriteLine(string.Format(LocalizableStrings.TemplatesNotValidGivenTheSpecifiedFilter, partialTemplatesMatchCount, string.Concat("type=", context)).Bold().Red()); anythingReported = true; } if (templateResolutionResult.RemainingPartialMatchGroups.Count > 0) { shouldShowTemplateList = true; } if (anythingReported) { Reporter.Error.WriteLine(); } }
public void TagFilterTests_TemplateWithTags(string templateTags, string testTag, MatchKind?kind) { const string separator = "||"; string[] templateTagsArray = templateTags.Split(separator); MockTemplateInfo template = new MockTemplateInfo("console", name: "Long name for Console App", identity: "Console.App.T1", groupIdentity: "Console.App.Test") .WithTag("language", "L1") .WithTag("type", "project") .WithBaselineInfo("app", "standard") .WithClassifications(templateTagsArray); var filter = WellKnownSearchFilters.ClassificationFilter(testTag); MatchInfo?result = filter(template); Assert.Equal(kind, result?.Kind); }
// Lists all the templates, filtered only by the context (item, project, etc) - and the host file. public static IReadOnlyCollection <ITemplateMatchInfo> PerformAllTemplatesInContextQuery(IReadOnlyList <ITemplateInfo> templateInfo, IHostSpecificDataLoader hostDataLoader, string context) { IReadOnlyList <FilterableTemplateInfo> filterableTemplateInfo = SetupFilterableTemplateInfoFromTemplateInfo(templateInfo); // If there is a context match, it must be exact. Other dispositions are irrelevant. Func <ITemplateMatchInfo, bool> contextFilter = x => x.MatchDisposition.All(d => d.Location != MatchLocation.Context || d.Kind == MatchKind.Exact); IReadOnlyCollection <ITemplateMatchInfo> templates = TemplateListFilter.GetTemplateMatchInfo ( filterableTemplateInfo, contextFilter, WellKnownSearchFilters.ContextFilter(context), WellKnownSearchFilters.NameFilter(string.Empty) ) .Where(x => !IsTemplateHiddenByHostFile(x.Info, hostDataLoader)).ToList(); return(templates); }
internal async Task Create_BasicTest_Package() { var bootstrapper = BootstrapperFactory.GetBootstrapper(); string packageLocation = _packageManager.PackProjectTemplatesNuGetPackage("microsoft.dotnet.common.projecttemplates.5.0"); bootstrapper.InstallTemplate(packageLocation); string output = TestHelper.CreateTemporaryFolder(); var template = bootstrapper.ListTemplates(true, WellKnownSearchFilters.NameFilter("console")); var result = await bootstrapper.CreateAsync(template.First().Info, "test", output, new Dictionary <string, string>(), false, "").ConfigureAwait(false); Assert.Equal(2, result.PrimaryOutputs.Count); Assert.Equal(2, result.PostActions.Count); Assert.True(File.Exists(Path.Combine(output, "Program.cs"))); Assert.True(File.Exists(Path.Combine(output, "test.csproj"))); }
private static void DisplayPartialNameMatchLanguageAndContextProblems(string templateName, string templateLanguage, string context, TemplateListResolutionResult templateResolutionResult, out bool shouldShowTemplateList) { shouldShowTemplateList = false; if (templateResolutionResult.IsNoTemplatesMatchedState || templateResolutionResult.UsingPartialMatches) { ShowNoTemplatesFoundMessage(templateName, templateLanguage, context); return; } bool anythingReported = false; foreach (IReadOnlyList <ITemplateMatchInfo> templateGroup in templateResolutionResult.ContextProblemMatchGroups) { // all templates in a group should have the same context & name if (templateGroup[0].Info.Tags != null && templateGroup[0].Info.Tags.TryGetValue("type", out ICacheTag typeTag)) { MatchInfo?matchInfo = WellKnownSearchFilters.ContextFilter(context)(templateGroup[0].Info); if ((matchInfo?.Kind ?? MatchKind.Mismatch) == MatchKind.Mismatch) { // {0} matches the specified name, but has been excluded by the --type parameter. Remove or change the --type parameter to use that template Reporter.Error.WriteLine(string.Format(LocalizableStrings.TemplateNotValidGivenTheSpecifiedFilter, templateGroup[0].Info.Name).Bold().Red()); anythingReported = true; } } else { // this really shouldn't ever happen. But better to have a generic error than quietly ignore the partial match. // //{0} cannot be created in the target location Reporter.Error.WriteLine(string.Format(LocalizableStrings.GenericPlaceholderTemplateContextError, templateGroup[0].Info.Name).Bold().Red()); anythingReported = true; } } if (templateResolutionResult.RemainingPartialMatchGroups.Count > 0) { shouldShowTemplateList = true; } if (anythingReported) { Reporter.Error.WriteLine(); } }
internal async Task Create_BasicTest_Package() { using Bootstrapper bootstrapper = BootstrapperFactory.GetBootstrapper(); string packageLocation = await _packageManager.GetNuGetPackage("Microsoft.DotNet.Common.ProjectTemplates.5.0").ConfigureAwait(false); await bootstrapper.InstallTemplateAsync(packageLocation).ConfigureAwait(false); string output = TestUtils.CreateTemporaryFolder(); var foundTemplates = await bootstrapper.GetTemplatesAsync(new[] { WellKnownSearchFilters.NameFilter("console") }).ConfigureAwait(false); var result = await bootstrapper.CreateAsync(foundTemplates[0].Info, "test", output, new Dictionary <string, string>(), false, "").ConfigureAwait(false); Assert.Equal(2, result.PrimaryOutputs.Count); Assert.Equal(2, result.PostActions.Count); Assert.True(File.Exists(Path.Combine(output, "Program.cs"))); Assert.True(File.Exists(Path.Combine(output, "test.csproj"))); }
// Query for template matches, filtered by everything available: name, language, context, parameters, and the host file. // this method is not used for list and help public static IReadOnlyCollection<ITemplateMatchInfo> PerformCoreTemplateQuery(IReadOnlyList<ITemplateInfo> templateInfo, IHostSpecificDataLoader hostDataLoader, INewCommandInput commandInput, string defaultLanguage) { IReadOnlyList<FilterableTemplateInfo> filterableTemplateInfo = SetupFilterableTemplateInfoFromTemplateInfo(templateInfo); IReadOnlyCollection<ITemplateMatchInfo> templates = TemplateListFilter.GetTemplateMatchInfo ( filterableTemplateInfo, TemplateListFilter.PartialMatchFilter, WellKnownSearchFilters.NameFilter(commandInput.TemplateName), WellKnownSearchFilters.ClassificationsFilter(commandInput.TemplateName), WellKnownSearchFilters.LanguageFilter(commandInput.Language), WellKnownSearchFilters.ContextFilter(commandInput.TypeFilter), WellKnownSearchFilters.BaselineFilter(commandInput.BaselineName) ) .Where(x => !IsTemplateHiddenByHostFile(x.Info, hostDataLoader)).ToList(); IReadOnlyList<ITemplateMatchInfo> coreMatchedTemplates = templates.Where(x => x.IsMatch).ToList(); if (coreMatchedTemplates.Count == 0) { // No exact matches, take the partial matches and be done. coreMatchedTemplates = templates.Where(x => x.IsPartialMatch).ToList(); } else { IReadOnlyList<ITemplateMatchInfo> matchesWithExactDispositionsInNameFields = coreMatchedTemplates.Where(x => x.MatchDisposition.Any(y => NameFields.Contains(y.Location) && y.Kind == MatchKind.Exact)).ToList(); if (matchesWithExactDispositionsInNameFields.Count > 0) { // Start with the exact name matches, if there are any. coreMatchedTemplates = matchesWithExactDispositionsInNameFields; } } if (string.IsNullOrEmpty(commandInput.Language) && !string.IsNullOrEmpty(defaultLanguage)) { // default language matching only makes sense if the user didn't specify a language. AddDefaultLanguageMatchingToTemplates(coreMatchedTemplates, defaultLanguage); } AddParameterMatchingToTemplates(coreMatchedTemplates, hostDataLoader, commandInput); return coreMatchedTemplates; }
internal async Task CreateTest_Package(string templateName, string parameters, MockCreationEffects expectedResult) { using Bootstrapper bootstrapper = BootstrapperFactory.GetBootstrapper(); string packageLocation = _packageManager.PackTestTemplatesNuGetPackage(); await bootstrapper.InstallTemplateAsync(packageLocation).ConfigureAwait(false); string name = BasicParametersParser.GetNameFromParameterString(parameters); string output = BasicParametersParser.GetOutputFromParameterString(parameters); Dictionary <string, string> parametersDict = BasicParametersParser.ParseParameterString(parameters); var foundTemplates = await bootstrapper.GetTemplatesAsync(new[] { WellKnownSearchFilters.NameFilter(templateName) }).ConfigureAwait(false); ITemplateInfo template = foundTemplates.Single(template => template.Info.ShortNameList.Contains($"TestAssets.{templateName}")).Info; var result = await bootstrapper.CreateAsync(template, name, output, parametersDict, false, "").ConfigureAwait(false); Assert.Equal(expectedResult.CreationResult.PrimaryOutputs.Count, result.PrimaryOutputs.Count); Assert.Equal( expectedResult.CreationResult.PrimaryOutputs.Select(po => po.Path).OrderBy(s => s, StringComparer.OrdinalIgnoreCase), result.PrimaryOutputs.Select(po => po.Path).OrderBy(s => s, StringComparer.OrdinalIgnoreCase), StringComparer.OrdinalIgnoreCase); foreach (string file in expectedResult.FileChanges.Where(fc => fc.ChangeKind != ChangeKind.Delete).Select(fc => fc.TargetRelativePath)) { string expectedFilePath = Path.Combine(output, file); Assert.True(File.Exists(expectedFilePath)); } foreach (string file in expectedResult.FileChanges.Where(fc => fc.ChangeKind == ChangeKind.Delete).Select(fc => fc.TargetRelativePath)) { string expectedFilePath = Path.Combine(output, file); Assert.False(File.Exists(expectedFilePath)); } foreach (string file in expectedResult.AbsentFiles) { string expectedFilePath = Path.Combine(output, file); Assert.False(File.Exists(expectedFilePath)); } foreach (string dir in expectedResult.AbsentDirectories) { string expectedPath = Path.Combine(output, dir); Assert.False(Directory.Exists(expectedPath)); } }
internal async Task CreateTest(string templateName, string parameters, MockCreationEffects expectedResult) { Bootstrapper bootstrapper = BootstrapperFactory.GetBootstrapper(); bootstrapper.InstallTestTemplate(templateName); string name = BasicParametersParser.GetNameFromParameterString(parameters); string output = BasicParametersParser.GetOutputFromParameterString(parameters); Dictionary <string, string> parametersDict = BasicParametersParser.ParseParameterString(parameters); ITemplateInfo template = bootstrapper.ListTemplates(true, WellKnownSearchFilters.NameFilter(templateName)).First().Info; var result = await bootstrapper.CreateAsync(template, name, output, parametersDict, false, "").ConfigureAwait(false); Assert.Equal(expectedResult.CreationResult.PrimaryOutputs.Count, result.PrimaryOutputs.Count); Assert.Equal( expectedResult.CreationResult.PrimaryOutputs.Select(po => po.Path).OrderBy(s => s, StringComparer.OrdinalIgnoreCase), result.PrimaryOutputs.Select(po => po.Path).OrderBy(s => s, StringComparer.OrdinalIgnoreCase), StringComparer.OrdinalIgnoreCase); foreach (string file in expectedResult.FileChanges.Where(fc => fc.ChangeKind != ChangeKind.Delete).Select(fc => fc.TargetRelativePath)) { string expectedFilePath = Path.Combine(output, file); Assert.True(File.Exists(expectedFilePath)); } foreach (string file in expectedResult.FileChanges.Where(fc => fc.ChangeKind == ChangeKind.Delete).Select(fc => fc.TargetRelativePath)) { string expectedFilePath = Path.Combine(output, file); Assert.False(File.Exists(expectedFilePath)); } foreach (string file in expectedResult.AbsentFiles) { string expectedFilePath = Path.Combine(output, file); Assert.False(File.Exists(expectedFilePath)); } foreach (string dir in expectedResult.AbsentDirectories) { string expectedPath = Path.Combine(output, dir); Assert.False(Directory.Exists(expectedPath)); } }
// Query for template matches, filtered by everything available: name, language, context, parameters, and the host file. public static TemplateListResolutionResult PerformCoreTemplateQuery(IReadOnlyList <ITemplateInfo> templateInfo, IHostSpecificDataLoader hostDataLoader, INewCommandInput commandInput, string defaultLanguage) { IReadOnlyCollection <IFilteredTemplateInfo> templates = TemplateListFilter.FilterTemplates ( templateInfo, false, WellKnownSearchFilters.NameFilter(commandInput.TemplateName), WellKnownSearchFilters.ClassificationsFilter(commandInput.TemplateName), WellKnownSearchFilters.LanguageFilter(commandInput.Language), WellKnownSearchFilters.ContextFilter(commandInput.TypeFilter?.ToLowerInvariant()), WellKnownSearchFilters.BaselineFilter(commandInput.BaselineName) ) .Where(x => !IsTemplateHiddenByHostFile(x.Info, hostDataLoader)).ToList(); IReadOnlyList <IFilteredTemplateInfo> coreMatchedTemplates = templates.Where(x => x.IsMatch).ToList(); bool anyExactCoreMatches; if (coreMatchedTemplates.Count == 0) { coreMatchedTemplates = templates.Where(x => x.IsPartialMatch).ToList(); anyExactCoreMatches = false; } else { anyExactCoreMatches = true; IReadOnlyList <IFilteredTemplateInfo> matchesWithExactDispositionsInNameFields = coreMatchedTemplates.Where(x => x.MatchDisposition.Any(y => NameFields.Contains(y.Location) && y.Kind == MatchKind.Exact)).ToList(); if (matchesWithExactDispositionsInNameFields.Count > 0) { coreMatchedTemplates = matchesWithExactDispositionsInNameFields; } } TemplateListResolutionResult matchResults = new TemplateListResolutionResult() { CoreMatchedTemplates = coreMatchedTemplates }; QueryForUnambiguousTemplateGroup(templateInfo, hostDataLoader, commandInput, matchResults, defaultLanguage, anyExactCoreMatches); return(matchResults); }
public static IReadOnlyCollection <ITemplateMatchInfo> PerformCoreTemplateQueryForList(IReadOnlyList <ITemplateInfo> templateInfo, IHostSpecificDataLoader hostDataLoader, INewCommandInput commandInput, string defaultLanguage) { IReadOnlyList <FilterableTemplateInfo> filterableTemplateInfo = SetupFilterableTemplateInfoFromTemplateInfo(templateInfo); // for list we also try to get match on template name in classification (tags). These matches only will be used if short name and name has a mismatch. // filter below only sets the exact or partial match if name matches the tag. If name doesn't match the tag, no match disposition is added to collection. IReadOnlyList <ITemplateMatchInfo> coreMatchedTemplates = TemplateListFilter.GetTemplateMatchInfo ( filterableTemplateInfo, TemplateListFilter.PartialMatchFilter, WellKnownSearchFilters.NameFilter(commandInput.TemplateName), WellKnownSearchFilters.ClassificationsFilter(commandInput.TemplateName), WellKnownSearchFilters.LanguageFilter(commandInput.Language), WellKnownSearchFilters.ContextFilter(commandInput.TypeFilter), WellKnownSearchFilters.BaselineFilter(commandInput.BaselineName) ) .Where(x => !IsTemplateHiddenByHostFile(x.Info, hostDataLoader)).ToList(); AddParameterMatchingToTemplates(coreMatchedTemplates, hostDataLoader, commandInput); return(coreMatchedTemplates); }
internal async Task Create_TemplateWithBinaryFile_Folder() { using Bootstrapper bootstrapper = BootstrapperFactory.GetBootstrapper(); string templateLocation = TestUtils.GetTestTemplateLocation("TemplateWithBinaryFile"); await bootstrapper.InstallTemplateAsync(templateLocation).ConfigureAwait(false); string output = TestUtils.CreateTemporaryFolder(); var foundTemplates = await bootstrapper.GetTemplatesAsync(new[] { WellKnownSearchFilters.NameFilter("TestAssets.TemplateWithBinaryFile") }).ConfigureAwait(false); var result = await bootstrapper.CreateAsync(foundTemplates[0].Info, "my-test-folder", output, new Dictionary <string, string>()).ConfigureAwait(false); string sourceImage = Path.Combine(templateLocation, "image.png"); string targetImage = Path.Combine(output, "image.png"); Assert.True(File.Exists(targetImage)); Assert.Equal( new FileInfo(sourceImage).Length, new FileInfo(targetImage).Length); Assert.True(TestUtils.CompareFiles(sourceImage, targetImage), $"The content of {sourceImage} and {targetImage} is not same."); }
/// <summary> /// Performs the filtering of installed templates for template instantiated. /// Filters applied: template name filter; language, type, classification and baseline filters. Only templates that match the filters are returned, no partial matches allowed. /// In case any templates in match above are matching name or short name exactly, only they are returned. /// The matches for default language and template specific parameters are added to the result. /// </summary> /// <param name="templateInfo">the list of templates to be filtered</param> /// <param name="hostDataLoader">data of the host</param> /// <param name="commandInput">new command data used in CLI</param> /// <param name="defaultLanguage"></param> /// <returns>the collection of the templates with their match dispositions (<seealso cref="ITemplateMatchInfo"/>). The templates that do not match are not added to the collection</returns> public static IReadOnlyCollection <ITemplateMatchInfo> PerformCoreTemplateQuery(IReadOnlyList <ITemplateInfo> templateInfo, IHostSpecificDataLoader hostDataLoader, INewCommandInput commandInput, string defaultLanguage) { IReadOnlyList <FilterableTemplateInfo> filterableTemplateInfo = SetupFilterableTemplateInfoFromTemplateInfo(templateInfo); IReadOnlyCollection <ITemplateMatchInfo> templates = TemplateListFilter.GetTemplateMatchInfo ( filterableTemplateInfo, TemplateListFilter.ExactMatchFilter, WellKnownSearchFilters.NameFilter(commandInput.TemplateName), //WellKnownSearchFilters.ClassificationsFilter(commandInput.TemplateName), WellKnownSearchFilters.LanguageFilter(commandInput.Language), WellKnownSearchFilters.ContextFilter(commandInput.TypeFilter), WellKnownSearchFilters.BaselineFilter(commandInput.BaselineName) ) .Where(x => !IsTemplateHiddenByHostFile(x.Info, hostDataLoader)).ToList(); //select only the templates which do not have mismatches //if any template has exact match for name - use those; otherwise partial name matches are also considered when resolving templates IReadOnlyList <ITemplateMatchInfo> matchesWithExactDispositionsInNameFields = templates.Where(x => x.MatchDisposition.Any(y => NameFields.Contains(y.Location) && y.Kind == MatchKind.Exact)).ToList(); if (matchesWithExactDispositionsInNameFields.Count > 0) { templates = matchesWithExactDispositionsInNameFields; } if (string.IsNullOrEmpty(commandInput.Language) && !string.IsNullOrEmpty(defaultLanguage)) { // add default language matches to the list // default language matching only makes sense if the user didn't specify a language. AddDefaultLanguageMatchingToTemplates(templates, defaultLanguage); } //add specific template parameters matches to the list AddParameterMatchingToTemplates(templates, hostDataLoader, commandInput); return(templates); }
internal static IReadOnlyCollection <ITemplateMatchInfo> PerformCoreTemplateQuery(IReadOnlyList <ITemplateInfo> templateInfo, IHostSpecificDataLoader hostDataLoader, INewCommandInput commandInput, string?defaultLanguage) { IReadOnlyList <ITemplateInfo> filterableTemplateInfo = SetupTemplateInfoWithGroupShortNames(templateInfo); #pragma warning disable CS0618 // Type or member is obsolete IReadOnlyCollection <ITemplateMatchInfo> templates = TemplateListFilter.GetTemplateMatchInfo( filterableTemplateInfo, WellKnownSearchFilters.MatchesAllCriteria, CliNameFilter(commandInput.TemplateName), WellKnownSearchFilters.LanguageFilter(commandInput.Language), WellKnownSearchFilters.TypeFilter(commandInput.TypeFilter), WellKnownSearchFilters.BaselineFilter(commandInput.BaselineName), CliDefaultLanguageFilter(defaultLanguage) ) #pragma warning restore CS0618 // Type or member is obsolete .Where(x => !IsTemplateHiddenByHostFile(x.Info, hostDataLoader)).ToList(); //select only the templates which do not have mismatches //if any template has exact match for name - use those; otherwise partial name matches are also considered when resolving templates IReadOnlyList <ITemplateMatchInfo> matchesWithExactDispositionsInNameFields = templates.Where( x => x.MatchDisposition.Any( y => (y.Name == MatchInfo.BuiltIn.Name || y.Name == MatchInfo.BuiltIn.ShortName) && y.Kind == MatchKind.Exact)).ToList(); if (matchesWithExactDispositionsInNameFields.Count > 0) { templates = matchesWithExactDispositionsInNameFields; } //add specific template parameters matches to the list AddParameterMatchingToTemplates(templates, hostDataLoader, commandInput); return(templates); }
private Task PerformCoreTemplateQueryAsync() { string context = DetermineTemplateContext(); //Perform the core query to search for templates IReadOnlyCollection <IFilteredTemplateInfo> templates = _templateCreator.List ( false, WellKnownSearchFilters.AliasFilter(TemplateName), WellKnownSearchFilters.NameFilter(TemplateName), WellKnownSearchFilters.ClassificationsFilter(TemplateName), WellKnownSearchFilters.LanguageFilter(Language), WellKnownSearchFilters.ContextFilter(context) ); IReadOnlyList <IFilteredTemplateInfo> matchedTemplates = templates.Where(x => x.IsMatch).ToList(); if (matchedTemplates.Count == 0) { matchedTemplates = templates.Where(x => x.IsPartialMatch).ToList(); _forceAmbiguousFlow = true; } else { IReadOnlyList <IFilteredTemplateInfo> matchesWithExactDispositionsInNameFields = matchedTemplates.Where(x => x.MatchDisposition.Any(y => NameFields.Contains(y.Location) && y.Kind == MatchKind.Exact)).ToList(); if (matchesWithExactDispositionsInNameFields.Count > 0) { matchedTemplates = matchesWithExactDispositionsInNameFields; } } _matchedTemplates = matchedTemplates; return(Task.FromResult(true)); }