diff --git a/src/Microsoft.AspNet.Mvc.Core/Internal/Routing/LinkGenerationDecisionTree.cs b/src/Microsoft.AspNet.Mvc.Core/Internal/Routing/LinkGenerationDecisionTree.cs
index 536e98caeb..d42cc18a9c 100644
--- a/src/Microsoft.AspNet.Mvc.Core/Internal/Routing/LinkGenerationDecisionTree.cs
+++ b/src/Microsoft.AspNet.Mvc.Core/Internal/Routing/LinkGenerationDecisionTree.cs
@@ -131,24 +131,24 @@ namespace Microsoft.AspNet.Mvc.Internal.Routing
public int Compare(LinkGenerationMatch x, LinkGenerationMatch y)
{
- // For these comparisons lower is better.
-
+ // For this comparison lower is better.
if (x.Entry.Order != y.Entry.Order)
{
return x.Entry.Order.CompareTo(y.Entry.Order);
}
+ if (x.Entry.GenerationPrecedence != y.Entry.GenerationPrecedence)
+ {
+ // Reversed because higher is better
+ return y.Entry.GenerationPrecedence.CompareTo(x.Entry.GenerationPrecedence);
+ }
+
if (x.IsFallbackMatch != y.IsFallbackMatch)
{
// A fallback match is worse than a non-fallback
return x.IsFallbackMatch.CompareTo(y.IsFallbackMatch);
}
- if (x.Entry.Precedence != y.Entry.Precedence)
- {
- return x.Entry.Precedence.CompareTo(y.Entry.Precedence);
- }
-
return StringComparer.Ordinal.Compare(x.Entry.TemplateText, y.Entry.TemplateText);
}
}
diff --git a/src/Microsoft.AspNet.Mvc.Core/Routing/AttributeRoute.cs b/src/Microsoft.AspNet.Mvc.Core/Routing/AttributeRoute.cs
index ade42dc0aa..f2954c9f4a 100644
--- a/src/Microsoft.AspNet.Mvc.Core/Routing/AttributeRoute.cs
+++ b/src/Microsoft.AspNet.Mvc.Core/Routing/AttributeRoute.cs
@@ -103,7 +103,7 @@ namespace Microsoft.AspNet.Mvc.Routing
Defaults = routeInfo.Defaults,
Constraints = routeInfo.Constraints,
Order = routeInfo.Order,
- Precedence = routeInfo.Precedence,
+ GenerationPrecedence = routeInfo.GenerationPrecedence,
RequiredLinkValues = routeInfo.ActionDescriptor.RouteValueDefaults,
RouteGroup = routeInfo.RouteGroup,
Template = routeInfo.ParsedTemplate,
@@ -122,7 +122,7 @@ namespace Microsoft.AspNet.Mvc.Routing
matchingEntries.Add(new AttributeRouteMatchingEntry()
{
Order = routeInfo.Order,
- Precedence = routeInfo.Precedence,
+ Precedence = routeInfo.MatchPrecedence,
Target = _target,
RouteName = routeInfo.Name,
RouteTemplate = routeInfo.RouteTemplate,
@@ -269,7 +269,8 @@ namespace Microsoft.AspNet.Mvc.Routing
routeInfo.Order = action.AttributeRouteInfo.Order;
- routeInfo.Precedence = AttributeRoutePrecedence.Compute(routeInfo.ParsedTemplate);
+ routeInfo.MatchPrecedence = AttributeRoutePrecedence.ComputeMatched(routeInfo.ParsedTemplate);
+ routeInfo.GenerationPrecedence = AttributeRoutePrecedence.ComputeGenerated(routeInfo.ParsedTemplate);
routeInfo.Name = action.AttributeRouteInfo.Name;
@@ -314,7 +315,9 @@ namespace Microsoft.AspNet.Mvc.Routing
public int Order { get; set; }
- public decimal Precedence { get; set; }
+ public decimal MatchPrecedence { get; set; }
+
+ public decimal GenerationPrecedence { get; set; }
public string RouteGroup { get; set; }
diff --git a/src/Microsoft.AspNet.Mvc.Core/Routing/AttributeRouteLinkGenerationEntry.cs b/src/Microsoft.AspNet.Mvc.Core/Routing/AttributeRouteLinkGenerationEntry.cs
index f971fd13ad..db86e27706 100644
--- a/src/Microsoft.AspNet.Mvc.Core/Routing/AttributeRouteLinkGenerationEntry.cs
+++ b/src/Microsoft.AspNet.Mvc.Core/Routing/AttributeRouteLinkGenerationEntry.cs
@@ -34,9 +34,9 @@ namespace Microsoft.AspNet.Mvc.Routing
public int Order { get; set; }
///
- /// The precedence of the template.
+ /// The precedence of the template for link generation. Greater number means higher precedence.
///
- public decimal Precedence { get; set; }
+ public decimal GenerationPrecedence { get; set; }
///
/// The name of the route.
diff --git a/src/Microsoft.AspNet.Mvc.Core/Routing/AttributeRoutePrecedence.cs b/src/Microsoft.AspNet.Mvc.Core/Routing/AttributeRoutePrecedence.cs
index d7601eb95c..2edde2f595 100644
--- a/src/Microsoft.AspNet.Mvc.Core/Routing/AttributeRoutePrecedence.cs
+++ b/src/Microsoft.AspNet.Mvc.Core/Routing/AttributeRoutePrecedence.cs
@@ -13,7 +13,12 @@ namespace Microsoft.AspNet.Mvc.Routing
///
public static class AttributeRoutePrecedence
{
- public static decimal Compute(RouteTemplate template)
+ // Compute the precedence for matching a provided url
+ // e.g.: /api/template == 1.1
+ // /api/template/{id} == 1.13
+ // /api/{id:int} == 1.2
+ // /api/template/{id:int} == 1.12
+ public static decimal ComputeMatched(RouteTemplate template)
{
// Each precedence digit corresponds to one decimal place. For example, 3 segments with precedences 2, 1,
// and 4 results in a combined precedence of 2.14 (decimal).
@@ -23,7 +28,31 @@ namespace Microsoft.AspNet.Mvc.Routing
{
var segment = template.Segments[i];
- var digit = ComputeDigit(segment);
+ var digit = ComputeMatchDigit(segment);
+ Debug.Assert(digit >= 0 && digit < 10);
+
+ precedence += decimal.Divide(digit, (decimal)Math.Pow(10, i));
+ }
+
+ return precedence;
+ }
+
+ // Compute the precedence for generating a url
+ // e.g.: /api/template == 5.5
+ // /api/template/{id} == 5.53
+ // /api/{id:int} == 5.4
+ // /api/template/{id:int} == 5.54
+ public static decimal ComputeGenerated(RouteTemplate template)
+ {
+ // Each precedence digit corresponds to one decimal place. For example, 3 segments with precedences 2, 1,
+ // and 4 results in a combined precedence of 2.14 (decimal).
+ var precedence = 0m;
+
+ for (var i = 0; i < template.Segments.Count; i++)
+ {
+ var segment = template.Segments[i];
+
+ var digit = ComputeGenerationDigit(segment);
Debug.Assert(digit >= 0 && digit < 10);
precedence += decimal.Divide(digit, (decimal)Math.Pow(10, i));
@@ -32,17 +61,49 @@ namespace Microsoft.AspNet.Mvc.Routing
return precedence;
}
+ // Segments have the following order:
+ // 5 - Literal segments
+ // 4 - Multi-part segments && Constrained parameter segments
+ // 3 - Unconstrained parameter segements
+ // 2 - Constrained wildcard parameter segments
+ // 1 - Unconstrained wildcard parameter segments
+ private static int ComputeGenerationDigit(TemplateSegment segment)
+ {
+ if(segment.Parts.Count > 1)
+ {
+ return 4;
+ }
+
+ var part = segment.Parts[0];
+ if(part.IsLiteral)
+ {
+ return 5;
+ }
+ else
+ {
+ Debug.Assert(part.IsParameter);
+ var digit = part.IsCatchAll ? 1 : 3;
+
+ if (part.InlineConstraints != null && part.InlineConstraints.Any())
+ {
+ digit++;
+ }
+
+ return digit;
+ }
+ }
+
// Segments have the following order:
// 1 - Literal segments
// 2 - Constrained parameter segments / Multi-part segments
// 3 - Unconstrained parameter segments
// 4 - Constrained wildcard parameter segments
// 5 - Unconstrained wildcard parameter segments
- private static int ComputeDigit(TemplateSegment segment)
+ private static int ComputeMatchDigit(TemplateSegment segment)
{
if (segment.Parts.Count > 1)
{
- // Multi-part segments should appear after literal segments but before parameter segments
+ // Multi-part segments should appear after literal segments and along with parameter segments
return 2;
}
diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/Internal/Routing/LinkGenerationDecisionTreeTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/Internal/Routing/LinkGenerationDecisionTreeTest.cs
index d48c7e0310..e55aaf5742 100644
--- a/test/Microsoft.AspNet.Mvc.Core.Test/Internal/Routing/LinkGenerationDecisionTreeTest.cs
+++ b/test/Microsoft.AspNet.Mvc.Core.Test/Internal/Routing/LinkGenerationDecisionTreeTest.cs
@@ -247,12 +247,12 @@ namespace Microsoft.AspNet.Mvc.Internal.Routing
var entries = new List();
var entry1 = CreateEntry(new { controller = "Store", action = "Buy" });
- entry1.Precedence = 1;
+ entry1.GenerationPrecedence = 0;
entries.Add(entry1);
var entry2 = CreateEntry(new { controller = "Store", action = "Buy" });
entry2.Order = 1;
- entry2.Precedence = 0;
+ entry2.GenerationPrecedence = 1;
entries.Add(entry2);
var tree = new LinkGenerationDecisionTree(entries);
@@ -274,11 +274,11 @@ namespace Microsoft.AspNet.Mvc.Internal.Routing
var entries = new List();
var entry1 = CreateEntry(new { controller = "Store", action = "Buy" });
- entry1.Precedence = 0;
+ entry1.GenerationPrecedence = 1;
entries.Add(entry1);
var entry2 = CreateEntry(new { controller = "Store", action = "Buy" });
- entry2.Precedence = 1;
+ entry2.GenerationPrecedence = 0;
entries.Add(entry2);
var tree = new LinkGenerationDecisionTree(entries);
diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/Routing/AttributeRoutePrecedenceTests.cs b/test/Microsoft.AspNet.Mvc.Core.Test/Routing/AttributeRoutePrecedenceTests.cs
index 14f91749fe..047b03f170 100644
--- a/test/Microsoft.AspNet.Mvc.Core.Test/Routing/AttributeRoutePrecedenceTests.cs
+++ b/test/Microsoft.AspNet.Mvc.Core.Test/Routing/AttributeRoutePrecedenceTests.cs
@@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
#if DNX451
+using System;
using Microsoft.AspNet.Routing;
using Microsoft.AspNet.Routing.Template;
using Microsoft.Extensions.OptionsModel;
@@ -16,11 +17,25 @@ namespace Microsoft.AspNet.Mvc.Routing
[InlineData("Employees/{id}", "Employees/{employeeId}")]
[InlineData("abc", "def")]
[InlineData("{x:alpha}", "{x:int}")]
- public void Compute_IsEqual(string xTemplate, string yTemplate)
+ public void ComputeMatched_IsEqual(string xTemplate, string yTemplate)
{
// Arrange & Act
- var xPrededence = Compute(xTemplate);
- var yPrededence = Compute(yTemplate);
+ var xPrededence = ComputeMatched(xTemplate);
+ var yPrededence = ComputeMatched(yTemplate);
+
+ // Assert
+ Assert.Equal(xPrededence, yPrededence);
+ }
+
+ [Theory]
+ [InlineData("Employees/{id}", "Employees/{employeeId}")]
+ [InlineData("abc", "def")]
+ [InlineData("{x:alpha}", "{x:int}")]
+ public void ComputeGenerated_IsEqual(string xTemplate, string yTemplate)
+ {
+ // Arrange & Act
+ var xPrededence = ComputeGenerated(xTemplate);
+ var yPrededence = ComputeGenerated(yTemplate);
// Assert
Assert.Equal(xPrededence, yPrededence);
@@ -47,23 +62,63 @@ namespace Microsoft.AspNet.Mvc.Routing
[InlineData("abc/{x:int}", "abc/{*x}")]
[InlineData("abc/{x}", "abc/{*x}")]
[InlineData("{x}/{y:int}", "{x}/{y}")]
- public void Compute_IsLessThan(string xTemplate, string yTemplate)
+ public void ComputeMatched_IsLessThan(string xTemplate, string yTemplate)
{
// Arrange & Act
- var xPrededence = Compute(xTemplate);
- var yPrededence = Compute(yTemplate);
+ var xPrededence = ComputeMatched(xTemplate);
+ var yPrededence = ComputeMatched(yTemplate);
// Assert
Assert.True(xPrededence < yPrededence);
}
- private static decimal Compute(string template)
+ [Theory]
+ [InlineData("abc", "a{x}")]
+ [InlineData("abc", "{x}c")]
+ [InlineData("abc", "{x:int}")]
+ [InlineData("abc", "{x}")]
+ [InlineData("abc", "{*x}")]
+ [InlineData("{x:int}", "{x}")]
+ [InlineData("{x:int}", "{*x}")]
+ [InlineData("a{x}", "{x}")]
+ [InlineData("{x}c", "{x}")]
+ [InlineData("a{x}", "{*x}")]
+ [InlineData("{x}c", "{*x}")]
+ [InlineData("{x}", "{*x}")]
+ [InlineData("{*x:maxlength(10)}", "{*x}")]
+ [InlineData("abc/def", "abc/{x:int}")]
+ [InlineData("abc/def", "abc/{x}")]
+ [InlineData("abc/def", "abc/{*x}")]
+ [InlineData("abc/{x:int}", "abc/{x}")]
+ [InlineData("abc/{x:int}", "abc/{*x}")]
+ [InlineData("abc/{x}", "abc/{*x}")]
+ [InlineData("{x}/{y:int}", "{x}/{y}")]
+ public void ComputeGenerated_IsGreaterThan(string xTemplate, string yTemplate)
+ {
+ // Arrange & Act
+ var xPrecedence = ComputeGenerated(xTemplate);
+ var yPrecedence = ComputeGenerated(yTemplate);
+
+ // Assert
+ Assert.True(xPrecedence > yPrecedence);
+ }
+
+ private static decimal ComputeMatched(string template)
+ {
+ return Compute(template, AttributeRoutePrecedence.ComputeMatched);
+ }
+ private static decimal ComputeGenerated(string template)
+ {
+ return Compute(template, AttributeRoutePrecedence.ComputeGenerated);
+ }
+
+ private static decimal Compute(string template, Func func)
{
var options = new Mock>();
options.SetupGet(o => o.Value).Returns(new RouteOptions());
var parsed = TemplateParser.Parse(template);
- return AttributeRoutePrecedence.Compute(parsed);
+ return func(parsed);
}
}
}
diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/Routing/InnerAttributeRouteTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/Routing/InnerAttributeRouteTest.cs
index b2b5f1cbf3..f95121c7f7 100644
--- a/test/Microsoft.AspNet.Mvc.Core.Test/Routing/InnerAttributeRouteTest.cs
+++ b/test/Microsoft.AspNet.Mvc.Core.Test/Routing/InnerAttributeRouteTest.cs
@@ -360,6 +360,167 @@ namespace Microsoft.AspNet.Mvc.Routing
Assert.False(context.IsHandled);
}
+ [Theory]
+ [InlineData("template", "{*url:alpha}", "/template?url=dingo&id=5")]
+ [InlineData("{*url:alpha}", "{*url}", "/dingo?id=5")]
+ [InlineData("{id}", "{*url}", "/5?url=dingo")]
+ [InlineData("{id}", "{*url:alpha}", "/5?url=dingo")]
+ [InlineData("{id:int}", "{id}", "/5?url=dingo")]
+ [InlineData("template/api/{*url}", "template/api", "/template/api/dingo?id=5")]
+ [InlineData("template/api", "template/{*url}", "/template/api?url=dingo&id=5")]
+ [InlineData("template/api", "template/api{id}location", "/template/api?url=dingo&id=5")]
+ [InlineData("template/api{id}location", "template/{id:int}", "/template/api5location?url=dingo")]
+ public void AttributeRoute_GenerateLink(string firstTemplate, string secondTemplate, string expectedPath)
+ {
+ // Arrange
+ var expectedGroup = CreateRouteGroup(0, firstTemplate);
+
+ string selectedGroup = null;
+ Action callback = ctx =>
+ {
+ selectedGroup = (string)ctx.ProvidedValues[AttributeRouting.RouteGroupKey];
+ ctx.IsBound = true;
+ };
+
+ var values = new Dictionary
+ {
+ {"url", "dingo" },
+ {"id", 5 }
+ };
+
+ var route = CreateAttributeRoute(callback, firstTemplate, secondTemplate);
+ var context = CreateVirtualPathContext(
+ values: values,
+ ambientValues: null);
+
+ // Act
+ var result = route.GetVirtualPath(context);
+
+ // Assert
+ Assert.NotNull(result);
+ Assert.Equal(new PathString(expectedPath), result.VirtualPath);
+ Assert.Same(route, result.Router);
+ Assert.Empty(result.DataTokens);
+ Assert.Equal(expectedGroup, selectedGroup);
+ }
+
+ [Fact]
+ public void AttributeRoute_GenerateLink_LongerTemplateWithDefaultIsMoreSpecific()
+ {
+ // Arrange
+ var firstTemplate = "template";
+ var secondTemplate = "template/{parameter:int=1003}";
+
+ var expectedGroup = CreateRouteGroup(0, secondTemplate);
+
+ string selectedGroup = null;
+ Action callback = ctx =>
+ {
+ selectedGroup = (string)ctx.ProvidedValues[AttributeRouting.RouteGroupKey];
+ ctx.IsBound = true;
+ };
+
+ var route = CreateAttributeRoute(callback, firstTemplate, secondTemplate);
+ var context = CreateVirtualPathContext(
+ values: null,
+ ambientValues: null);
+
+ // Act
+ var result = route.GetVirtualPath(context);
+
+ // Assert
+ Assert.NotNull(result);
+ // The Binder binds to /template
+ Assert.Equal(new PathString($"/template"), result.VirtualPath);
+ Assert.Same(route, result.Router);
+ Assert.Empty(result.DataTokens);
+ // Even though the path was /template, the group generated from was /template/{paramter:int=1003}
+ Assert.Equal(expectedGroup, selectedGroup);
+ }
+
+ [Theory]
+ [InlineData("template/{parameter:int=5}", "template", "/template/5")]
+ [InlineData("template/{parameter}", "template", "/template/5")]
+ [InlineData("template/{parameter}/{id}", "template/{parameter}", "/template/5/1234")]
+ public void AttributeRoute_GenerateLink_OrderingAgnostic(
+ string firstTemplate,
+ string secondTemplate,
+ string expectedPath)
+ {
+ var expectedGroup = CreateRouteGroup(0, firstTemplate);
+
+ string selectedGroup = null;
+ Action callback = ctx =>
+ {
+ selectedGroup = (string)ctx.ProvidedValues[AttributeRouting.RouteGroupKey];
+ ctx.IsBound = true;
+ };
+
+ var route = CreateAttributeRoute(callback, firstTemplate, secondTemplate);
+ var parameter = 5;
+ var id = 1234;
+ var values = new Dictionary
+ {
+ { nameof(parameter) , parameter},
+ { nameof(id), id }
+ };
+ var context = CreateVirtualPathContext(
+ values: null,
+ ambientValues: values);
+
+ // Act
+ var result = route.GetVirtualPath(context);
+
+ // Assert
+ Assert.NotNull(result);
+ Assert.Equal(new PathString(expectedPath), result.VirtualPath);
+ Assert.Same(route, result.Router);
+ Assert.Empty(result.DataTokens);
+ Assert.Equal(expectedGroup, selectedGroup);
+ }
+
+ [Theory]
+ [InlineData("template", "template/{parameter}", "/template/5")]
+ [InlineData("template/{parameter}", "template/{parameter}/{id}", "/template/5/1234")]
+ [InlineData("template", "template/{parameter:int=5}", "/template/5")]
+ public void AttributeRoute_GenerateLink_UseAvailableVariables(
+ string firstTemplate,
+ string secondTemplate,
+ string expectedPath)
+ {
+ // Arrange
+ var expectedGroup = CreateRouteGroup(0, secondTemplate);
+
+ string selectedGroup = null;
+ Action callback = ctx =>
+ {
+ selectedGroup = (string)ctx.ProvidedValues[AttributeRouting.RouteGroupKey];
+ ctx.IsBound = true;
+ };
+
+ var route = CreateAttributeRoute(callback, firstTemplate, secondTemplate);
+ var parameter = 5;
+ var id = 1234;
+ var values = new Dictionary
+ {
+ { nameof(parameter) , parameter},
+ { nameof(id), id }
+ };
+ var context = CreateVirtualPathContext(
+ values: null,
+ ambientValues: values);
+
+ // Act
+ var result = route.GetVirtualPath(context);
+
+ // Assert
+ Assert.NotNull(result);
+ Assert.Equal(new PathString(expectedPath), result.VirtualPath);
+ Assert.Same(route, result.Router);
+ Assert.Empty(result.DataTokens);
+ Assert.Equal(expectedGroup, selectedGroup);
+ }
+
[Theory]
[InlineData("template/5", "template/{parameter:int}")]
[InlineData("template/5", "template/{parameter}")]
@@ -1228,10 +1389,10 @@ namespace Microsoft.AspNet.Mvc.Routing
{
// Arrange
var entry1 = CreateGenerationEntry("Help/Store", new { area = "Help", action = "Edit", controller = "Store" });
- entry1.Precedence = 1;
+ entry1.GenerationPrecedence = 2;
var entry2 = CreateGenerationEntry("Store", new { area = (string)null, action = "Edit", controller = "Store" });
- entry2.Precedence = 2;
+ entry2.GenerationPrecedence = 1;
var next = new StubRouter();
@@ -1254,10 +1415,10 @@ namespace Microsoft.AspNet.Mvc.Routing
{
// Arrange
var entry1 = CreateGenerationEntry("Help/Store", new { area = "Help", action = "Edit", controller = "Store" });
- entry1.Precedence = 2;
+ entry1.GenerationPrecedence = 1;
var entry2 = CreateGenerationEntry("Store", new { area = (string)null, action = "Edit", controller = "Store" });
- entry2.Precedence = 1;
+ entry2.GenerationPrecedence = 2;
var next = new StubRouter();
@@ -1280,10 +1441,10 @@ namespace Microsoft.AspNet.Mvc.Routing
{
// Arrange
var entry1 = CreateGenerationEntry("Help/Store", new { area = "Help", action = "Edit", controller = "Store" });
- entry1.Precedence = 1;
+ entry1.GenerationPrecedence = 2;
var entry2 = CreateGenerationEntry("Store", new { area = (string)null, action = "Edit", controller = "Store" });
- entry2.Precedence = 2;
+ entry2.GenerationPrecedence = 1;
var next = new StubRouter();
@@ -1308,10 +1469,10 @@ namespace Microsoft.AspNet.Mvc.Routing
{
// Arrange
var entry1 = CreateGenerationEntry("Help/Store", new { area = "Help", action = "Edit", controller = "Store" });
- entry1.Precedence = 1;
+ entry1.GenerationPrecedence = 2;
var entry2 = CreateGenerationEntry("Store", new { area = (string)null, action = "Edit", controller = "Store" });
- entry2.Precedence = 2;
+ entry2.GenerationPrecedence = 1;
var next = new StubRouter();
@@ -1645,7 +1806,7 @@ namespace Microsoft.AspNet.Mvc.Routing
entry.TemplateMatcher = new TemplateMatcher(
parsedRouteTemplate,
new RouteValueDictionary(new { test_route_group = routeGroup }));
- entry.Precedence = AttributeRoutePrecedence.Compute(parsedRouteTemplate);
+ entry.Precedence = AttributeRoutePrecedence.ComputeMatched(parsedRouteTemplate);
entry.Order = order;
entry.Constraints = GetRouteConstriants(CreateConstraintResolver(), template, parsedRouteTemplate);
return entry;
@@ -1690,7 +1851,7 @@ namespace Microsoft.AspNet.Mvc.Routing
entry.Defaults = defaults;
entry.Binder = new TemplateBinder(entry.Template, defaults);
entry.Order = order;
- entry.Precedence = AttributeRoutePrecedence.Compute(entry.Template);
+ entry.GenerationPrecedence = AttributeRoutePrecedence.ComputeGenerated(entry.Template);
entry.RequiredLinkValues = new RouteValueDictionary(requiredValues);
entry.RouteGroup = CreateRouteGroup(order, template);
entry.Name = name;
@@ -1779,6 +1940,25 @@ namespace Microsoft.AspNet.Mvc.Routing
version: 1);
}
+ private static InnerAttributeRoute CreateAttributeRoute(
+ Action virtualPathCallback,
+ string firstTemplate,
+ string secondTemplate)
+ {
+ var next = new Mock();
+ next.Setup(n => n.GetVirtualPath(It.IsAny())).Callback(virtualPathCallback)
+ .Returns((VirtualPathData)null);
+
+ var matchingRoutes = Enumerable.Empty();
+ var firstEntry = CreateGenerationEntry(firstTemplate, requiredValues: null);
+ var secondEntry = CreateGenerationEntry(secondTemplate, requiredValues: null);
+
+ return CreateAttributeRoute(
+ next.Object,
+ matchingRoutes,
+ new[] { secondEntry, firstEntry });
+ }
+
private static InnerAttributeRoute CreateRoutingAttributeRoute(
ILoggerFactory loggerFactory = null,
params AttributeRouteMatchingEntry[] entries)
diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/Routing/UrlHelperTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/Routing/UrlHelperTest.cs
index 55db9b69b2..b7ea579ef7 100644
--- a/test/Microsoft.AspNet.Mvc.Core.Test/Routing/UrlHelperTest.cs
+++ b/test/Microsoft.AspNet.Mvc.Core.Test/Routing/UrlHelperTest.cs
@@ -741,7 +741,7 @@ namespace Microsoft.AspNet.Mvc.Routing
[Fact]
public void Action_RouteValueInvalidation_DoesNotAffectActionAndController()
{
- // Arrage
+ // Arrange
var services = GetServices();
var routeBuilder = new RouteBuilder()
{
@@ -986,7 +986,6 @@ namespace Microsoft.AspNet.Mvc.Routing
return services.Object;
}
-
private static IRouter GetRouter(
IServiceProvider services,
string mockRouteName,
diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/LocalizationTest.cs b/test/Microsoft.AspNet.Mvc.FunctionalTests/LocalizationTest.cs
index 952c5be3ae..5dc2b6071a 100644
--- a/test/Microsoft.AspNet.Mvc.FunctionalTests/LocalizationTest.cs
+++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/LocalizationTest.cs
@@ -1,6 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Reflection;
@@ -79,16 +80,16 @@ mypartial
get
{
var expected1 =
-@"Hello there!!
-Learn More
-Hi John ! You are in 2015 year and today is Thursday";
+ "Hello there!!" + Environment.NewLine +
+ "Learn More" + Environment.NewLine +
+ "Hi John ! You are in 2015 year and today is Thursday";
yield return new[] {"en-GB", expected1 };
var expected2 =
-@"Bonjour!
-apprendre Encore Plus
-Salut John ! Vous êtes en 2015 an aujourd'hui est Thursday";
+ "Bonjour!" + Environment.NewLine +
+ "apprendre Encore Plus" + Environment.NewLine +
+ "Salut John ! Vous êtes en 2015 an aujourd'hui est Thursday";
yield return new[] { "fr", expected2 };
}
}
diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/RoutingTests.cs b/test/Microsoft.AspNet.Mvc.FunctionalTests/RoutingTests.cs
index be11efa8eb..1b23fb489a 100644
--- a/test/Microsoft.AspNet.Mvc.FunctionalTests/RoutingTests.cs
+++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/RoutingTests.cs
@@ -500,7 +500,7 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
public async Task AttributeRoutedAction_AcceptVerbsAndRouteTemplate_IsReachable(string verb, string path)
{
// Arrange
- var expectedUrl = "/Bank";
+ var expectedUrl = "/Bank/Update";
var message = new HttpRequestMessage(new HttpMethod(verb), "http://localhost/" + path);
// Act