예제 #1
0
        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);
        }
예제 #2
0
        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);
        }
예제 #3
0
        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); });
        }
예제 #4
0
        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);
        }
예제 #5
0
        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);
        }
예제 #6
0
        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);
        }
예제 #7
0
        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);
        }
예제 #8
0
        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);
        }
예제 #9
0
        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;
        }
예제 #11
0
        /// <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;
        }