diff --git a/src/Microsoft.AspNetCore.Routing/Matching/Candidate.cs b/src/Microsoft.AspNetCore.Routing/Matching/Candidate.cs index 47564d511c..4493293251 100644 --- a/src/Microsoft.AspNetCore.Routing/Matching/Candidate.cs +++ b/src/Microsoft.AspNetCore.Routing/Matching/Candidate.cs @@ -3,13 +3,14 @@ using System; using System.Collections.Generic; +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing.Patterns; namespace Microsoft.AspNetCore.Routing.Matching { internal readonly struct Candidate { - public readonly RouteEndpoint Endpoint; + public readonly Endpoint Endpoint; // Used to optimize out operations that modify route values. public readonly CandidateFlags Flags; @@ -48,7 +49,7 @@ namespace Microsoft.AspNetCore.Routing.Matching public readonly int Score; // Used in tests. - public Candidate(RouteEndpoint endpoint) + public Candidate(Endpoint endpoint) { Endpoint = endpoint; @@ -63,7 +64,7 @@ namespace Microsoft.AspNetCore.Routing.Matching } public Candidate( - RouteEndpoint endpoint, + Endpoint endpoint, int score, KeyValuePair[] slots, (string parameterName, int segmentIndex, int slotIndex)[] captures, diff --git a/src/Microsoft.AspNetCore.Routing/Matching/CandidateSet.cs b/src/Microsoft.AspNetCore.Routing/Matching/CandidateSet.cs index 58cb607e41..6b2e66ab56 100644 --- a/src/Microsoft.AspNetCore.Routing/Matching/CandidateSet.cs +++ b/src/Microsoft.AspNetCore.Routing/Matching/CandidateSet.cs @@ -36,7 +36,7 @@ namespace Microsoft.AspNetCore.Routing.Matching /// /// The list of endpoints, sorted in descending priority order. /// The list of endpoint scores. . - public CandidateSet(RouteEndpoint[] endpoints, int[] scores) + public CandidateSet(Endpoint[] endpoints, int[] scores) { Count = endpoints.Length; diff --git a/src/Microsoft.AspNetCore.Routing/Matching/CandidateState.cs b/src/Microsoft.AspNetCore.Routing/Matching/CandidateState.cs index 04913a532d..8cc0c1319e 100644 --- a/src/Microsoft.AspNetCore.Routing/Matching/CandidateState.cs +++ b/src/Microsoft.AspNetCore.Routing/Matching/CandidateState.cs @@ -1,6 +1,8 @@ // 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 Microsoft.AspNetCore.Http; + namespace Microsoft.AspNetCore.Routing.Matching { /// @@ -8,7 +10,7 @@ namespace Microsoft.AspNetCore.Routing.Matching /// public struct CandidateState { - internal CandidateState(RouteEndpoint endpoint, int score) + internal CandidateState(Endpoint endpoint, int score) { Endpoint = endpoint; Score = score; @@ -20,7 +22,7 @@ namespace Microsoft.AspNetCore.Routing.Matching /// /// Gets the . /// - public RouteEndpoint Endpoint { get; } + public Endpoint Endpoint { get; } /// /// Gets the score of the within the current diff --git a/src/Microsoft.AspNetCore.Routing/Matching/DefaultEndpointSelector.cs b/src/Microsoft.AspNetCore.Routing/Matching/DefaultEndpointSelector.cs index e503e2f769..c2128d0e7a 100644 --- a/src/Microsoft.AspNetCore.Routing/Matching/DefaultEndpointSelector.cs +++ b/src/Microsoft.AspNetCore.Routing/Matching/DefaultEndpointSelector.cs @@ -48,7 +48,7 @@ namespace Microsoft.AspNetCore.Routing.Matching EndpointFeature feature, CandidateSet candidateSet) { - RouteEndpoint endpoint = null; + Endpoint endpoint = null; RouteValueDictionary values = null; int? foundScore = null; for (var i = 0; i < candidateSet.Count; i++) @@ -96,7 +96,7 @@ namespace Microsoft.AspNetCore.Routing.Matching { // If we get here it's the result of an ambiguity - we're OK with this // being a littler slower and more allocatey. - var matches = new List(); + var matches = new List(); for (var i = 0; i < candidates.Count; i++) { ref var state = ref candidates[i]; diff --git a/src/Microsoft.AspNetCore.Routing/Matching/DfaMatcherBuilder.cs b/src/Microsoft.AspNetCore.Routing/Matching/DfaMatcherBuilder.cs index 10b05a60a0..040d39a5e6 100644 --- a/src/Microsoft.AspNetCore.Routing/Matching/DfaMatcherBuilder.cs +++ b/src/Microsoft.AspNetCore.Routing/Matching/DfaMatcherBuilder.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing.Patterns; namespace Microsoft.AspNetCore.Routing.Matching @@ -16,7 +17,7 @@ namespace Microsoft.AspNetCore.Routing.Matching private readonly EndpointSelector _selector; private readonly MatcherPolicy[] _policies; private readonly INodeBuilderPolicy[] _nodeBuilders; - private readonly RouteEndpointComparer _comparer; + private readonly EndpointComparer _comparer; public DfaMatcherBuilder( ParameterPolicyFactory parameterPolicyFactory, @@ -29,7 +30,7 @@ namespace Microsoft.AspNetCore.Routing.Matching // Taking care to use _policies, which has been sorted. _nodeBuilders = _policies.OfType().ToArray(); - _comparer = new RouteEndpointComparer(_policies.OfType().ToArray()); + _comparer = new EndpointComparer(_policies.OfType().ToArray()); } public override void AddEndpoint(RouteEndpoint endpoint) @@ -337,7 +338,7 @@ namespace Microsoft.AspNetCore.Routing.Matching // Builds an array of candidates for a node, assigns a 'score' for each // endpoint. - internal Candidate[] CreateCandidates(IReadOnlyList endpoints) + internal Candidate[] CreateCandidates(IReadOnlyList endpoints) { if (endpoints.Count == 0) { @@ -367,76 +368,80 @@ namespace Microsoft.AspNetCore.Routing.Matching } // internal for tests - internal Candidate CreateCandidate(RouteEndpoint endpoint, int score) + internal Candidate CreateCandidate(Endpoint endpoint, int score) { var assignments = new Dictionary(StringComparer.OrdinalIgnoreCase); var slots = new List>(); var captures = new List<(string parameterName, int segmentIndex, int slotIndex)>(); (string parameterName, int segmentIndex, int slotIndex) catchAll = default; - foreach (var kvp in endpoint.RoutePattern.Defaults) - { - assignments.Add(kvp.Key, assignments.Count); - slots.Add(kvp); - } - - for (var i = 0; i < endpoint.RoutePattern.PathSegments.Count; i++) - { - var segment = endpoint.RoutePattern.PathSegments[i]; - if (!segment.IsSimple) - { - continue; - } - - var parameterPart = segment.Parts[0] as RoutePatternParameterPart; - if (parameterPart == null) - { - continue; - } - - if (!assignments.TryGetValue(parameterPart.Name, out var slotIndex)) - { - slotIndex = assignments.Count; - assignments.Add(parameterPart.Name, slotIndex); - - var hasDefaultValue = parameterPart.Default != null || parameterPart.IsCatchAll; - slots.Add(hasDefaultValue ? new KeyValuePair(parameterPart.Name, parameterPart.Default) : default); - } - - if (parameterPart.IsCatchAll) - { - catchAll = (parameterPart.Name, i, slotIndex); - } - else - { - captures.Add((parameterPart.Name, i, slotIndex)); - } - } - var complexSegments = new List<(RoutePatternPathSegment pathSegment, int segmentIndex)>(); - for (var i = 0; i < endpoint.RoutePattern.PathSegments.Count; i++) + var constraints = new List>(); + + if (endpoint is RouteEndpoint routeEndpoint) { - var segment = endpoint.RoutePattern.PathSegments[i]; - if (segment.IsSimple) + foreach (var kvp in routeEndpoint.RoutePattern.Defaults) { - continue; + assignments.Add(kvp.Key, assignments.Count); + slots.Add(kvp); } - complexSegments.Add((segment, i)); - } - - var constraints = new List>(); - foreach (var kvp in endpoint.RoutePattern.ParameterPolicies) - { - var parameter = endpoint.RoutePattern.GetParameter(kvp.Key); // may be null, that's ok - var parameterPolicyReferences = kvp.Value; - for (var i = 0; i < parameterPolicyReferences.Count; i++) + for (var i = 0; i < routeEndpoint.RoutePattern.PathSegments.Count; i++) { - var reference = parameterPolicyReferences[i]; - var parameterPolicy = _parameterPolicyFactory.Create(parameter, reference); - if (parameterPolicy is IRouteConstraint routeConstraint) + var segment = routeEndpoint.RoutePattern.PathSegments[i]; + if (!segment.IsSimple) { - constraints.Add(new KeyValuePair(kvp.Key, routeConstraint)); + continue; + } + + var parameterPart = segment.Parts[0] as RoutePatternParameterPart; + if (parameterPart == null) + { + continue; + } + + if (!assignments.TryGetValue(parameterPart.Name, out var slotIndex)) + { + slotIndex = assignments.Count; + assignments.Add(parameterPart.Name, slotIndex); + + var hasDefaultValue = parameterPart.Default != null || parameterPart.IsCatchAll; + slots.Add(hasDefaultValue ? new KeyValuePair(parameterPart.Name, parameterPart.Default) : default); + } + + if (parameterPart.IsCatchAll) + { + catchAll = (parameterPart.Name, i, slotIndex); + } + else + { + captures.Add((parameterPart.Name, i, slotIndex)); + } + } + + for (var i = 0; i < routeEndpoint.RoutePattern.PathSegments.Count; i++) + { + var segment = routeEndpoint.RoutePattern.PathSegments[i]; + if (segment.IsSimple) + { + continue; + } + + complexSegments.Add((segment, i)); + } + + foreach (var kvp in routeEndpoint.RoutePattern.ParameterPolicies) + { + var parameter = routeEndpoint.RoutePattern.GetParameter(kvp.Key); // may be null, that's ok + var parameterPolicyReferences = kvp.Value; + for (var i = 0; i < parameterPolicyReferences.Count; i++) + { + var reference = parameterPolicyReferences[i]; + var parameterPolicy = _parameterPolicyFactory.Create(parameter, reference); + if (parameterPolicy is IRouteConstraint routeConstraint) + { + constraints.Add(new KeyValuePair(kvp.Key, routeConstraint)); + } } } } @@ -549,8 +554,7 @@ namespace Microsoft.AspNetCore.Routing.Matching Label = parent.Label + " " + edge.State.ToString(), }; - // TODO: https://github.com/aspnet/Routing/issues/648 - next.Matches.AddRange(edge.Endpoints.Cast().ToArray()); + next.Matches.AddRange(edge.Endpoints); nextWork.Add(next); parent.PolicyEdges.Add(edge.State, next); diff --git a/src/Microsoft.AspNetCore.Routing/Matching/DfaNode.cs b/src/Microsoft.AspNetCore.Routing/Matching/DfaNode.cs index 36cd4e9bcd..e68c4909f7 100644 --- a/src/Microsoft.AspNetCore.Routing/Matching/DfaNode.cs +++ b/src/Microsoft.AspNetCore.Routing/Matching/DfaNode.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text; +using Microsoft.AspNetCore.Http; namespace Microsoft.AspNetCore.Routing.Matching { @@ -16,7 +17,7 @@ namespace Microsoft.AspNetCore.Routing.Matching public DfaNode() { Literals = new Dictionary(StringComparer.OrdinalIgnoreCase); - Matches = new List(); + Matches = new List(); PolicyEdges = new Dictionary(); } @@ -29,7 +30,7 @@ namespace Microsoft.AspNetCore.Routing.Matching // Just for diagnostics and debugging public string Label { get; set; } - public List Matches { get; } + public List Matches { get; } public Dictionary Literals { get; } diff --git a/src/Microsoft.AspNetCore.Routing/Matching/EndpointComparer.cs b/src/Microsoft.AspNetCore.Routing/Matching/EndpointComparer.cs new file mode 100644 index 0000000000..4609b8c4f3 --- /dev/null +++ b/src/Microsoft.AspNetCore.Routing/Matching/EndpointComparer.cs @@ -0,0 +1,163 @@ +// 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.Diagnostics; +using Microsoft.AspNetCore.Http; + +namespace Microsoft.AspNetCore.Routing.Matching +{ + // Use to sort and group Endpoints. RouteEndpoints are sorted before other implementations. + // + // NOTE: + // When ordering endpoints, we compare the route templates as an absolute last resort. + // This is used as a factor to ensure that we always have a predictable ordering + // for tests, errors, etc. + // + // When we group endpoints we don't consider the route template, because we're trying + // to group endpoints not separate them. + // + // TLDR: + // IComparer implementation considers the template string as a tiebreaker. + // IEqualityComparer implementation does not. + // This is cool and good. + internal class EndpointComparer : IComparer, IEqualityComparer + { + private readonly IComparer[] _comparers; + + public EndpointComparer(IEndpointComparerPolicy[] policies) + { + // Order, Precedence, (others)... + _comparers = new IComparer[2 + policies.Length]; + _comparers[0] = OrderComparer.Instance; + _comparers[1] = PrecedenceComparer.Instance; + for (var i = 0; i < policies.Length; i++) + { + _comparers[i + 2] = policies[i].Comparer; + } + } + + public int Compare(Endpoint x, Endpoint y) + { + // We don't expose this publicly, and we should never call it on + // a null endpoint. + Debug.Assert(x != null); + Debug.Assert(y != null); + + var compare = CompareCore(x, y); + + // Since we're sorting, use the route template as a last resort. + return compare == 0 ? ComparePattern(x, y) : compare; + } + + private int ComparePattern(Endpoint x, Endpoint y) + { + // A RouteEndpoint always comes before a non-RouteEndpoint, regardless of its RawText value + var routeEndpointX = x as RouteEndpoint; + var routeEndpointY = y as RouteEndpoint; + + if (routeEndpointX != null) + { + if (routeEndpointY != null) + { + return routeEndpointX.RoutePattern.RawText.CompareTo(routeEndpointY.RoutePattern.RawText); + } + + return 1; + } + else if (routeEndpointY != null) + { + return -1; + } + + return 0; + } + + public bool Equals(Endpoint x, Endpoint y) + { + // We don't expose this publicly, and we should never call it on + // a null endpoint. + Debug.Assert(x != null); + Debug.Assert(y != null); + + return CompareCore(x, y) == 0; + } + + public int GetHashCode(Endpoint obj) + { + // This should not be possible to call publicly. + Debug.Fail("We don't expect this to be called."); + throw new System.NotImplementedException(); + } + + private int CompareCore(Endpoint x, Endpoint y) + { + for (var i = 0; i < _comparers.Length; i++) + { + var compare = _comparers[i].Compare(x, y); + if (compare != 0) + { + return compare; + } + } + + return 0; + } + + private class OrderComparer : IComparer + { + public static readonly IComparer Instance = new OrderComparer(); + + public int Compare(Endpoint x, Endpoint y) + { + var routeEndpointX = x as RouteEndpoint; + var routeEndpointY = y as RouteEndpoint; + + if (routeEndpointX != null) + { + if (routeEndpointY != null) + { + return routeEndpointX.Order.CompareTo(routeEndpointY.Order); + } + + return 1; + } + else if (routeEndpointY != null) + { + return -1; + } + + return 0; + } + } + + private class PrecedenceComparer : IComparer + { + public static readonly IComparer Instance = new PrecedenceComparer(); + + public int Compare(Endpoint x, Endpoint y) + { + var routeEndpointX = x as RouteEndpoint; + var routeEndpointY = y as RouteEndpoint; + + if (routeEndpointX != null) + { + if (routeEndpointY != null) + { + return routeEndpointX.RoutePattern.InboundPrecedence + .CompareTo(routeEndpointY.RoutePattern.InboundPrecedence); + } + + return 1; + } + else if (routeEndpointY != null) + { + return -1; + } + + return 0; + } + } + } +} diff --git a/src/Microsoft.AspNetCore.Routing/Matching/HttpMethodMatcherPolicy.cs b/src/Microsoft.AspNetCore.Routing/Matching/HttpMethodMatcherPolicy.cs index 64601108e7..86706fed0d 100644 --- a/src/Microsoft.AspNetCore.Routing/Matching/HttpMethodMatcherPolicy.cs +++ b/src/Microsoft.AspNetCore.Routing/Matching/HttpMethodMatcherPolicy.cs @@ -250,15 +250,13 @@ namespace Microsoft.AspNetCore.Routing.Matching private Endpoint CreateRejectionEndpoint(IEnumerable httpMethods) { var allow = string.Join(", ", httpMethods); - return new RouteEndpoint( + return new Endpoint( (context) => { context.Response.StatusCode = 405; context.Response.Headers.Add("Allow", allow); return Task.CompletedTask; }, - RoutePatternFactory.Parse("/"), - 0, EndpointMetadataCollection.Empty, Http405EndpointDisplayName); } diff --git a/src/Microsoft.AspNetCore.Routing/Matching/RouteEndpointComparer.cs b/src/Microsoft.AspNetCore.Routing/Matching/RouteEndpointComparer.cs deleted file mode 100644 index 95c897c373..0000000000 --- a/src/Microsoft.AspNetCore.Routing/Matching/RouteEndpointComparer.cs +++ /dev/null @@ -1,103 +0,0 @@ -// 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.Collections.Generic; -using System.Diagnostics; - -namespace Microsoft.AspNetCore.Routing.Matching -{ - // Use to sort and group RouteEndpoints. - // - // NOTE: - // When ordering endpoints, we compare the route templates as an absolute last resort. - // This is used as a factor to ensure that we always have a predictable ordering - // for tests, errors, etc. - // - // When we group endpoints we don't consider the route template, because we're trying - // to group endpoints not separate them. - // - // TLDR: - // IComparer implementation considers the template string as a tiebreaker. - // IEqualityComparer implementation does not. - // This is cool and good. - internal class RouteEndpointComparer : IComparer, IEqualityComparer - { - private readonly IComparer[] _comparers; - - public RouteEndpointComparer(IEndpointComparerPolicy[] policies) - { - // Order, Precedence, (others)... - _comparers = new IComparer[2 + policies.Length]; - _comparers[0] = OrderComparer.Instance; - _comparers[1] = PrecedenceComparer.Instance; - for (var i = 0; i < policies.Length; i++) - { - _comparers[i + 2] = policies[i].Comparer; - } - } - - public int Compare(RouteEndpoint x, RouteEndpoint y) - { - // We don't expose this publicly, and we should never call it on - // a null endpoint. - Debug.Assert(x != null); - Debug.Assert(y != null); - - var compare = CompareCore(x, y); - - // Since we're sorting, use the route template as a last resort. - return compare == 0 ? x.RoutePattern.RawText.CompareTo(y.RoutePattern.RawText) : compare; - } - - public bool Equals(RouteEndpoint x, RouteEndpoint y) - { - // We don't expose this publicly, and we should never call it on - // a null endpoint. - Debug.Assert(x != null); - Debug.Assert(y != null); - - return CompareCore(x, y) == 0; - } - - public int GetHashCode(RouteEndpoint obj) - { - // This should not be possible to call publicly. - Debug.Fail("We don't expect this to be called."); - throw new System.NotImplementedException(); - } - - private int CompareCore(RouteEndpoint x, RouteEndpoint y) - { - for (var i = 0; i < _comparers.Length; i++) - { - var compare = _comparers[i].Compare(x, y); - if (compare != 0) - { - return compare; - } - } - - return 0; - } - - private class OrderComparer : IComparer - { - public static readonly IComparer Instance = new OrderComparer(); - - public int Compare(RouteEndpoint x, RouteEndpoint y) - { - return x.Order.CompareTo(y.Order); - } - } - - private class PrecedenceComparer : IComparer - { - public static readonly IComparer Instance = new PrecedenceComparer(); - - public int Compare(RouteEndpoint x, RouteEndpoint y) - { - return x.RoutePattern.InboundPrecedence.CompareTo(y.RoutePattern.InboundPrecedence); - } - } - } -} diff --git a/test/Microsoft.AspNetCore.Routing.Tests/Matching/DfaMatcherBuilderTest.cs b/test/Microsoft.AspNetCore.Routing.Tests/Matching/DfaMatcherBuilderTest.cs index 430a2d92d9..a9071c7962 100644 --- a/test/Microsoft.AspNetCore.Routing.Tests/Matching/DfaMatcherBuilderTest.cs +++ b/test/Microsoft.AspNetCore.Routing.Tests/Matching/DfaMatcherBuilderTest.cs @@ -595,6 +595,54 @@ namespace Microsoft.AspNetCore.Routing.Matching Assert.Empty(test1_1.PolicyEdges); } + [Fact] + public void BuildDfaTree_WithPolicies_AndBranches_NonRouteEndpoint() + { + // Arrange + var builder = CreateDfaMatcherBuilder(new TestNonRoutePatternMatcherPolicy()); + + var endpoint1 = CreateEndpoint("/a", metadata: new object[] { new TestMetadata1(0), }); + builder.AddEndpoint(endpoint1); + + var endpoint2 = CreateEndpoint("/a", metadata: new object[] { new TestMetadata1(1), }); + builder.AddEndpoint(endpoint2); + + var endpoint3 = CreateEndpoint("/a", metadata: new object[] { new TestMetadata1(1), }); + builder.AddEndpoint(endpoint3); + + // Act + var root = builder.BuildDfaTree(); + + // Assert + Assert.Empty(root.Matches); + Assert.Null(root.Parameters); + + var next = Assert.Single(root.Literals); + Assert.Equal("a", next.Key); + + var a = next.Value; + Assert.Empty(a.Matches); + Assert.IsType(a.NodeBuilder); + Assert.Collection( + a.PolicyEdges.OrderBy(e => e.Key), + e => Assert.Equal(0, e.Key), + e => Assert.Equal(1, e.Key), + e => Assert.Equal(int.MaxValue, e.Key)); + + var test1_0 = a.PolicyEdges[0]; + Assert.Equal(new[] { endpoint1, }, test1_0.Matches); + Assert.Null(test1_0.NodeBuilder); + Assert.Empty(test1_0.PolicyEdges); + + var test1_1 = a.PolicyEdges[1]; + Assert.Equal(new[] { endpoint2, endpoint3, }, test1_1.Matches); + Assert.Null(test1_1.NodeBuilder); + Assert.Empty(test1_1.PolicyEdges); + + var nonRouteEndpoint = a.PolicyEdges[int.MaxValue]; + Assert.Equal("MaxValueEndpoint", Assert.Single(nonRouteEndpoint.Matches).DisplayName); + } + [Fact] public void BuildDfaTree_WithPolicies_AndBranches_BothPoliciesSkipped() { @@ -968,5 +1016,35 @@ namespace Microsoft.AspNetCore.Routing.Matching .ToArray(); } } + + private class TestNonRoutePatternMatcherPolicy : MatcherPolicy, IEndpointComparerPolicy, INodeBuilderPolicy + { + public override int Order => 100; + + public IComparer Comparer => EndpointMetadataComparer.Default; + + public bool AppliesToNode(IReadOnlyList endpoints) + { + return endpoints.Any(e => e.Metadata.GetMetadata() != null); + } + + public PolicyJumpTable BuildJumpTable(int exitDestination, IReadOnlyList edges) + { + throw new NotImplementedException(); + } + + public IReadOnlyList GetEdges(IReadOnlyList endpoints) + { + var edges = endpoints + .GroupBy(e => e.Metadata.GetMetadata().State) + .Select(g => new PolicyNodeEdge(g.Key, g.ToArray())) + .ToList(); + + var maxValueEndpoint = new Endpoint(TestConstants.EmptyRequestDelegate, EndpointMetadataCollection.Empty, "MaxValueEndpoint"); + edges.Add(new PolicyNodeEdge(int.MaxValue, new[] { maxValueEndpoint })); + + return edges; + } + } } } diff --git a/test/Microsoft.AspNetCore.Routing.Tests/Matching/RouteEndpointComparerTest.cs b/test/Microsoft.AspNetCore.Routing.Tests/Matching/RouteEndpointComparerTest.cs index 92d6e22e68..b6f44d8186 100644 --- a/test/Microsoft.AspNetCore.Routing.Tests/Matching/RouteEndpointComparerTest.cs +++ b/test/Microsoft.AspNetCore.Routing.Tests/Matching/RouteEndpointComparerTest.cs @@ -228,9 +228,9 @@ namespace Microsoft.AspNetCore.Routing.Matching "test: " + template); } - private static RouteEndpointComparer CreateComparer(params IEndpointComparerPolicy[] policies) + private static EndpointComparer CreateComparer(params IEndpointComparerPolicy[] policies) { - return new RouteEndpointComparer(policies); + return new EndpointComparer(policies); } private class TestMetadata1