public void GetMatches_LinkToPageFromController_WithActionValueAmbiguous() { // Arrange var entries = new List <OutboundMatch>(); var entry1 = CreateMatch(new { controller = "Home", action = "Index", area = (string)null, page = (string)null, }); entry1.Entry.RouteTemplate = TemplateParser.Parse("a"); entries.Add(entry1); var entry2 = CreateMatch(new { page = "/Store/Buy", area = (string)null, controller = (string)null, action = (string)null, }); entry2.Entry.RouteTemplate = TemplateParser.Parse("b"); entries.Add(entry2); var tree = new LinkGenerationDecisionTree(entries); var context = CreateContext(new { page = "/Store/Buy", action = "Index", }, new { controller = "Home", action = "Index", page = "16", }); // Act var matches = tree.GetMatches(context.Values, context.AmbientValues).Select(m => m.Match).ToList(); // Assert Assert.Empty(matches); }
public void ToDebuggerDisplayString_GivesAFlattenedTree() { // Arrange var entries = new List <OutboundMatch>(); entries.Add(CreateMatch(new { action = "Buy", controller = "Store", version = "V1" }, "Store/Buy/V1")); entries.Add(CreateMatch(new { action = "Buy", controller = "Store", area = "Admin" }, "Admin/Store/Buy")); entries.Add(CreateMatch(new { action = "Buy", controller = "Products" }, "Products/Buy")); entries.Add(CreateMatch(new { action = "Buy", controller = "Store", version = "V2" }, "Store/Buy/V2")); entries.Add(CreateMatch(new { action = "Cart", controller = "Store" }, "Store/Cart")); entries.Add(CreateMatch(new { action = "Index", controller = "Home" }, "Home/Index/{id?}")); var tree = new LinkGenerationDecisionTree(entries); var newLine = Environment.NewLine; var expected = " => action: Buy => controller: Store => version: V1 (Matches: Store/Buy/V1)" + newLine + " => action: Buy => controller: Store => version: V2 (Matches: Store/Buy/V2)" + newLine + " => action: Buy => controller: Store => area: Admin (Matches: Admin/Store/Buy)" + newLine + " => action: Buy => controller: Products (Matches: Products/Buy)" + newLine + " => action: Cart => controller: Store (Matches: Store/Cart)" + newLine + " => action: Index => controller: Home (Matches: Home/Index/{id?})" + newLine; // Act var flattenedTree = tree.DebuggerDisplayString; // Assert Assert.Equal(expected, flattenedTree); }
public void GetMatches_PagesWithArea_AllValuesAmbient() { // Arrange var entries = new List <OutboundMatch>(); var entry1 = CreateMatch(new { page = "/Store/Buy", area = (string)null, }); entry1.Entry.RouteTemplate = TemplateParser.Parse("a"); entries.Add(entry1); var entry2 = CreateMatch(new { page = "/Store/Buy", area = "Admin" }); entry2.Entry.RouteTemplate = TemplateParser.Parse("b"); entries.Add(entry2); var tree = new LinkGenerationDecisionTree(entries); var context = CreateContext(new { }, new { page = "/Store/Buy", area = "Admin", }); // Act var matches = tree.GetMatches(context.Values, context.AmbientValues).Select(m => m.Match).ToList(); // Assert Assert.Collection( matches, m => { Assert.Same(entry2, m); }, m => { Assert.Same(entry1, m); }); }
public void SelectMultipleEntries_BothMatch_OrderedByTemplate() { // Arrange var entries = new List <OutboundMatch>(); var entry1 = CreateMatch(new { controller = "Store", action = "Buy" }); entry1.Entry.RouteTemplate = TemplateParser.Parse("a"); entries.Add(entry1); var entry2 = CreateMatch(new { controller = "Store", action = "Buy" }); entry2.Entry.RouteTemplate = TemplateParser.Parse("b"); entries.Add(entry2); var tree = new LinkGenerationDecisionTree(entries); var context = CreateContext(new { controller = "Store", action = "Buy" }); // Act var matches = tree.GetMatches(context.Values, context.AmbientValues).Select(m => m.Match).ToList(); // Assert Assert.Equal(entries, matches); }
public void SelectMultipleEntries_BothMatch_CriteriaSubset() { // Arrange var entries = new List <OutboundMatch>(); var entry1 = CreateMatch(new { controller = "Store", action = "Buy" }); entries.Add(entry1); var entry2 = CreateMatch(new { controller = "Store" }); entry2.Entry.Order = 1; entries.Add(entry2); var tree = new LinkGenerationDecisionTree(entries); var context = CreateContext( values: new { controller = "Store" }, ambientValues: new { controller = "Store", action = "Buy" }); // Act var matches = tree.GetMatches(context.Values, context.AmbientValues).Select(m => m.Match).ToList(); // Assert Assert.Equal(entries, matches); }
public void SelectMultipleEntries_OneDoesntMatch() { // Arrange var entries = new List <OutboundMatch>(); var entry1 = CreateMatch(new { controller = "Store", action = "Buy" }); entries.Add(entry1); var entry2 = CreateMatch(new { controller = "Store", action = "Cart" }); entries.Add(entry2); var tree = new LinkGenerationDecisionTree(entries); var context = CreateContext( values: new { controller = "Store" }, ambientValues: new { controller = "Store", action = "Buy" }); // Act var matches = tree.GetMatches(context.Values, context.AmbientValues); // Assert Assert.Same(entry1, Assert.Single(matches).Match); }
public void SelectSingleEntry_MultipleCriteria() { // Arrange var entries = new List <OutboundMatch>(); var entry = CreateMatch(new { controller = "Store", action = "Buy" }); entries.Add(entry); var tree = new LinkGenerationDecisionTree(entries); var context = CreateContext(new { controller = "Store", action = "Buy" }); // Act var matches = tree.GetMatches(context.Values, context.AmbientValues); // Assert Assert.Same(entry, Assert.Single(matches).Match); }
public void GetMatches_AllowsNullAmbientValues() { // Arrange var entries = new List <OutboundMatch>(); var entry = CreateMatch(new { }); entries.Add(entry); var tree = new LinkGenerationDecisionTree(entries); var context = CreateContext(new { }); // Act var matches = tree.GetMatches(context.Values, ambientValues: null); // Assert Assert.Same(entry, Assert.Single(matches).Match); }
public void SelectSingleEntry_MultipleCriteria_AmbientValue_Ignored() { // Arrange var entries = new List <OutboundMatch>(); var entry = CreateMatch(new { controller = "Store", action = (string)null }); entries.Add(entry); var tree = new LinkGenerationDecisionTree(entries); var context = CreateContext( values: new { controller = "Store" }, ambientValues: new { controller = "Store", action = "Buy" }); // Act var matches = tree.GetMatches(context.Values, context.AmbientValues); // Assert var match = Assert.Single(matches); Assert.Same(entry, match.Match); Assert.True(match.IsFallbackMatch); }
/// <summary> /// Creates a new <see cref="TreeRouter"/>. /// </summary> /// <param name="trees">The list of <see cref="UrlMatchingTree"/> that contains the route entries.</param> /// <param name="linkGenerationEntries">The set of <see cref="OutboundRouteEntry"/>.</param> /// <param name="urlEncoder">The <see cref="UrlEncoder"/>.</param> /// <param name="objectPool">The <see cref="ObjectPool{T}"/>.</param> /// <param name="routeLogger">The <see cref="ILogger"/> instance.</param> /// <param name="constraintLogger">The <see cref="ILogger"/> instance used /// in <see cref="RouteConstraintMatcher"/>.</param> /// <param name="version">The version of this route.</param> public TreeRouter( UrlMatchingTree[] trees, IEnumerable <OutboundRouteEntry> linkGenerationEntries, UrlEncoder urlEncoder, ObjectPool <UriBuildingContext> objectPool, ILogger routeLogger, ILogger constraintLogger, int version) { if (trees == null) { throw new ArgumentNullException(nameof(trees)); } if (linkGenerationEntries == null) { throw new ArgumentNullException(nameof(linkGenerationEntries)); } if (urlEncoder == null) { throw new ArgumentNullException(nameof(urlEncoder)); } if (objectPool == null) { throw new ArgumentNullException(nameof(objectPool)); } if (routeLogger == null) { throw new ArgumentNullException(nameof(routeLogger)); } if (constraintLogger == null) { throw new ArgumentNullException(nameof(constraintLogger)); } _trees = trees; _logger = routeLogger; _constraintLogger = constraintLogger; _namedEntries = new Dictionary <string, OutboundMatch>(StringComparer.OrdinalIgnoreCase); var outboundMatches = new List <OutboundMatch>(); foreach (var entry in linkGenerationEntries) { var binder = new TemplateBinder(urlEncoder, objectPool, entry.RouteTemplate, entry.Defaults); var outboundMatch = new OutboundMatch() { Entry = entry, TemplateBinder = binder }; outboundMatches.Add(outboundMatch); // Skip unnamed entries if (entry.RouteName == null) { continue; } // We only need to keep one OutboundMatch per route template // so in case two entries have the same name and the same template we only keep // the first entry. OutboundMatch namedMatch; if (_namedEntries.TryGetValue(entry.RouteName, out namedMatch) && !string.Equals( namedMatch.Entry.RouteTemplate.TemplateText, entry.RouteTemplate.TemplateText, StringComparison.OrdinalIgnoreCase)) { throw new ArgumentException( Resources.FormatAttributeRoute_DifferentLinkGenerationEntries_SameName(entry.RouteName), nameof(linkGenerationEntries)); } else if (namedMatch == null) { _namedEntries.Add(entry.RouteName, outboundMatch); } } // The decision tree will take care of ordering for these entries. _linkGenerationTree = new LinkGenerationDecisionTree(outboundMatches.ToArray()); Version = version; }
/// <summary> /// Creates a new <see cref="TreeRouter"/>. /// </summary> /// <param name="next">The next router. Invoked when a route entry matches.</param> /// <param name="trees">The list of <see cref="UrlMatchingTree"/> that contains the route entries.</param> /// <param name="linkGenerationEntries">The set of <see cref="TreeRouteLinkGenerationEntry"/>.</param> /// <param name="routeLogger">The <see cref="ILogger"/> instance.</param> /// <param name="constraintLogger">The <see cref="ILogger"/> instance used /// in <see cref="RouteConstraintMatcher"/>.</param> /// <param name="version">The version of this route.</param> public TreeRouter( IRouter next, UrlMatchingTree[] trees, IEnumerable <TreeRouteLinkGenerationEntry> linkGenerationEntries, ILogger routeLogger, ILogger constraintLogger, int version) { if (next == null) { throw new ArgumentNullException(nameof(next)); } if (trees == null) { throw new ArgumentNullException(nameof(trees)); } if (linkGenerationEntries == null) { throw new ArgumentNullException(nameof(linkGenerationEntries)); } if (routeLogger == null) { throw new ArgumentNullException(nameof(routeLogger)); } if (constraintLogger == null) { throw new ArgumentNullException(nameof(constraintLogger)); } _next = next; _trees = trees; _logger = routeLogger; _constraintLogger = constraintLogger; var namedEntries = new Dictionary <string, TreeRouteLinkGenerationEntry>( StringComparer.OrdinalIgnoreCase); foreach (var entry in linkGenerationEntries) { // Skip unnamed entries if (entry.Name == null) { continue; } // We only need to keep one AttributeRouteLinkGenerationEntry per route template // so in case two entries have the same name and the same template we only keep // the first entry. TreeRouteLinkGenerationEntry namedEntry = null; if (namedEntries.TryGetValue(entry.Name, out namedEntry) && !namedEntry.Template.TemplateText.Equals(entry.Template.TemplateText, StringComparison.OrdinalIgnoreCase)) { throw new ArgumentException( Resources.FormatAttributeRoute_DifferentLinkGenerationEntries_SameName(entry.Name), nameof(linkGenerationEntries)); } else if (namedEntry == null) { namedEntries.Add(entry.Name, entry); } } _namedEntries = namedEntries; // The decision tree will take care of ordering for these entries. _linkGenerationTree = new LinkGenerationDecisionTree(linkGenerationEntries.ToArray()); Version = version; }