Merge pull request #846 from dotnet-maestro-bot/merge/release/2.2-to-master

[automated] Merge branch 'release/2.2' => 'master'
This commit is contained in:
Ryan Nowak 2018-10-08 18:05:41 -07:00 committed by GitHub
commit 93dc6dd5f5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 180 additions and 101 deletions

View File

@ -8,10 +8,10 @@ using Microsoft.Extensions.DependencyInjection;
namespace Microsoft.AspNetCore.Routing.LinkGeneration namespace Microsoft.AspNetCore.Routing.LinkGeneration
{ {
public class SingleRouteRouteValuesBasedEndpointFinderBenchmark : EndpointRoutingBenchmarkBase public class SingleRouteRouteValuesAddressSchemeBenchmark : EndpointRoutingBenchmarkBase
{ {
private IEndpointFinder<RouteValuesAddress> _finder; private IEndpointAddressScheme<RouteValuesAddress> _implementation;
private TestEndpointFinder _baseFinder; private TestAddressScheme _baseline;
private (HttpContext HttpContext, RouteValueDictionary AmbientValues) _requestContext; private (HttpContext HttpContext, RouteValueDictionary AmbientValues) _requestContext;
[GlobalSetup] [GlobalSetup]
@ -23,8 +23,8 @@ namespace Microsoft.AspNetCore.Routing.LinkGeneration
SetupEndpoints(CreateEndpoint(template, defaults, requiredValues: requiredValues, routeName: "ProductDetails")); SetupEndpoints(CreateEndpoint(template, defaults, requiredValues: requiredValues, routeName: "ProductDetails"));
var services = CreateServices(); var services = CreateServices();
_finder = services.GetRequiredService<IEndpointFinder<RouteValuesAddress>>(); _implementation = services.GetRequiredService<IEndpointAddressScheme<RouteValuesAddress>>();
_baseFinder = new TestEndpointFinder(Endpoints[0]); _baseline = new TestAddressScheme(Endpoints[0]);
_requestContext = CreateCurrentRequestContext(); _requestContext = CreateCurrentRequestContext();
} }
@ -32,13 +32,13 @@ namespace Microsoft.AspNetCore.Routing.LinkGeneration
[Benchmark(Baseline = true)] [Benchmark(Baseline = true)]
public void Baseline() public void Baseline()
{ {
var actual = _baseFinder.FindEndpoints(address: 0); var actual = _baseline.FindEndpoints(address: 0);
} }
[Benchmark] [Benchmark]
public void RouteValues() public void RouteValues()
{ {
var actual = _finder.FindEndpoints(new RouteValuesAddress var actual = _implementation.FindEndpoints(new RouteValuesAddress
{ {
AmbientValues = _requestContext.AmbientValues, AmbientValues = _requestContext.AmbientValues,
ExplicitValues = new RouteValueDictionary(new { controller = "Products", action = "Details" }), ExplicitValues = new RouteValueDictionary(new { controller = "Products", action = "Details" }),
@ -49,18 +49,18 @@ namespace Microsoft.AspNetCore.Routing.LinkGeneration
[Benchmark] [Benchmark]
public void RouteName() public void RouteName()
{ {
var actual = _finder.FindEndpoints(new RouteValuesAddress var actual = _implementation.FindEndpoints(new RouteValuesAddress
{ {
AmbientValues = _requestContext.AmbientValues, AmbientValues = _requestContext.AmbientValues,
RouteName = "ProductDetails" RouteName = "ProductDetails"
}); });
} }
private class TestEndpointFinder : IEndpointFinder<int> private class TestAddressScheme : IEndpointAddressScheme<int>
{ {
private readonly Endpoint _endpoint; private readonly Endpoint _endpoint;
public TestEndpointFinder(Endpoint endpoint) public TestAddressScheme(Endpoint endpoint)
{ {
_endpoint = endpoint; _endpoint = endpoint;
} }

View File

@ -202,7 +202,7 @@ namespace Microsoft.AspNetCore.Routing
private List<RouteEndpoint> GetEndpoints<TAddress>(TAddress address) private List<RouteEndpoint> GetEndpoints<TAddress>(TAddress address)
{ {
var addressingScheme = _serviceProvider.GetRequiredService<IEndpointFinder<TAddress>>(); var addressingScheme = _serviceProvider.GetRequiredService<IEndpointAddressScheme<TAddress>>();
var endpoints = addressingScheme.FindEndpoints(address).OfType<RouteEndpoint>().ToList(); var endpoints = addressingScheme.FindEndpoints(address).OfType<RouteEndpoint>().ToList();
if (endpoints.Count == 0) if (endpoints.Count == 0)

View File

@ -75,8 +75,8 @@ namespace Microsoft.Extensions.DependencyInjection
// Link generation related services // Link generation related services
services.TryAddSingleton<LinkGenerator, DefaultLinkGenerator>(); services.TryAddSingleton<LinkGenerator, DefaultLinkGenerator>();
services.TryAddSingleton<IEndpointFinder<string>, EndpointNameEndpointFinder>(); services.TryAddSingleton<IEndpointAddressScheme<string>, EndpointNameAddressScheme>();
services.TryAddSingleton<IEndpointFinder<RouteValuesAddress>, RouteValuesBasedEndpointFinder>(); services.TryAddSingleton<IEndpointAddressScheme<RouteValuesAddress>, RouteValuesAddressScheme>();
// //
// Endpoint Selection // Endpoint Selection

View File

@ -9,11 +9,11 @@ using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Routing namespace Microsoft.AspNetCore.Routing
{ {
internal class EndpointNameEndpointFinder : IEndpointFinder<string> internal class EndpointNameAddressScheme : IEndpointAddressScheme<string>
{ {
private readonly DataSourceDependentCache<Dictionary<string, Endpoint[]>> _cache; private readonly DataSourceDependentCache<Dictionary<string, Endpoint[]>> _cache;
public EndpointNameEndpointFinder(CompositeEndpointDataSource dataSource) public EndpointNameAddressScheme(CompositeEndpointDataSource dataSource)
{ {
_cache = new DataSourceDependentCache<Dictionary<string, Endpoint[]>>(dataSource, Initialize); _cache = new DataSourceDependentCache<Dictionary<string, Endpoint[]>>(dataSource, Initialize);
} }
@ -94,7 +94,7 @@ namespace Microsoft.AspNetCore.Routing
string GetEndpointName(Endpoint endpoint) string GetEndpointName(Endpoint endpoint)
{ {
if (endpoint.Metadata.GetMetadata<ISuppressLinkGenerationMetadata>() != null) if (endpoint.Metadata.GetMetadata<ISuppressLinkGenerationMetadata>()?.SuppressLinkGeneration == true)
{ {
// Skip anything that's suppressed for linking. // Skip anything that's suppressed for linking.
return null; return null;

View File

@ -7,13 +7,13 @@ using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Routing namespace Microsoft.AspNetCore.Routing
{ {
/// <summary> /// <summary>
/// Defines a contract to find endpoints based on the supplied lookup information. /// Defines a contract to find endpoints based on the provided address.
/// </summary> /// </summary>
/// <typeparam name="TAddress">The address type to look up endpoints.</typeparam> /// <typeparam name="TAddress">The address type to look up endpoints.</typeparam>
public interface IEndpointFinder<TAddress> public interface IEndpointAddressScheme<TAddress>
{ {
/// <summary> /// <summary>
/// Finds endpoints based on the supplied lookup information. /// Finds endpoints based on the provided <paramref name="address"/>.
/// </summary> /// </summary>
/// <param name="address">The information used to look up endpoints.</param> /// <param name="address">The information used to look up endpoints.</param>
/// <returns>A collection of <see cref="Endpoint"/>.</returns> /// <returns>A collection of <see cref="Endpoint"/>.</returns>

View File

@ -4,10 +4,14 @@
namespace Microsoft.AspNetCore.Routing namespace Microsoft.AspNetCore.Routing
{ {
/// <summary> /// <summary>
/// Represents metadata used during link generation. /// Represents metadata used during link generation. If <see cref="SuppressLinkGeneration"/> is <c>true</c>
/// The associated endpoint will not be considered for link generation. /// the associated endpoint will not be used for link generation.
/// </summary> /// </summary>
public interface ISuppressLinkGenerationMetadata public interface ISuppressLinkGenerationMetadata
{ {
/// <summary>
/// Gets a value indicating whether the assocated endpoint should be used for link generation.
/// </summary>
bool SuppressLinkGeneration { get; }
} }
} }

View File

@ -4,10 +4,14 @@
namespace Microsoft.AspNetCore.Routing namespace Microsoft.AspNetCore.Routing
{ {
/// <summary> /// <summary>
/// Metadata used to prevent URL matching. The associated endpoint will not be /// Metadata used to prevent URL matching. If <see cref="SuppressMatching"/> is <c>true</c> the
/// considered URL matching for incoming requests. /// associated endpoint will not be considered for URL matching.
/// </summary> /// </summary>
public interface ISuppressMatchingMetadata public interface ISuppressMatchingMetadata
{ {
/// <summary>
/// Gets a value indicating whether the assocated endpoint should be used for URL matching.
/// </summary>
bool SuppressMatching { get; }
} }
} }

View File

@ -38,7 +38,7 @@ namespace Microsoft.AspNetCore.Routing.Internal
var endpoints = dataSource.Endpoints; var endpoints = dataSource.Endpoints;
for (var i = 0; i < endpoints.Count; i++) for (var i = 0; i < endpoints.Count; i++)
{ {
if (endpoints[i] is RouteEndpoint endpoint && endpoint.Metadata.GetMetadata<ISuppressMatchingMetadata>() == null) if (endpoints[i] is RouteEndpoint endpoint && endpoint.Metadata.GetMetadata<ISuppressMatchingMetadata>()?.SuppressMatching == true)
{ {
builder.AddEndpoint(endpoint); builder.AddEndpoint(endpoint);
} }

View File

@ -40,7 +40,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
// By design we only look at RouteEndpoint here. It's possible to // By design we only look at RouteEndpoint here. It's possible to
// register other endpoint types, which are non-routable, and it's // register other endpoint types, which are non-routable, and it's
// ok that we won't route to them. // ok that we won't route to them.
if (endpoints[i] is RouteEndpoint endpoint && endpoint.Metadata.GetMetadata<ISuppressMatchingMetadata>() == null) if (endpoints[i] is RouteEndpoint endpoint && endpoint.Metadata.GetMetadata<ISuppressMatchingMetadata>()?.SuppressMatching != true)
{ {
builder.AddEndpoint(endpoint); builder.AddEndpoint(endpoint);
} }

View File

@ -12,13 +12,13 @@ using Microsoft.Extensions.Primitives;
namespace Microsoft.AspNetCore.Routing namespace Microsoft.AspNetCore.Routing
{ {
internal class RouteValuesBasedEndpointFinder : IEndpointFinder<RouteValuesAddress> internal class RouteValuesAddressScheme : IEndpointAddressScheme<RouteValuesAddress>
{ {
private readonly CompositeEndpointDataSource _dataSource; private readonly CompositeEndpointDataSource _dataSource;
private LinkGenerationDecisionTree _allMatchesLinkGenerationTree; private LinkGenerationDecisionTree _allMatchesLinkGenerationTree;
private IDictionary<string, List<OutboundMatchResult>> _namedMatchResults; private IDictionary<string, List<OutboundMatchResult>> _namedMatchResults;
public RouteValuesBasedEndpointFinder(CompositeEndpointDataSource dataSource) public RouteValuesAddressScheme(CompositeEndpointDataSource dataSource)
{ {
_dataSource = dataSource; _dataSource = dataSource;
@ -102,9 +102,7 @@ namespace Microsoft.AspNetCore.Routing
var endpoints = _dataSource.Endpoints.OfType<RouteEndpoint>(); var endpoints = _dataSource.Endpoints.OfType<RouteEndpoint>();
foreach (var endpoint in endpoints) foreach (var endpoint in endpoints)
{ {
// Do not consider an endpoint for link generation if the following marker metadata is on it if (endpoint.Metadata.GetMetadata<ISuppressLinkGenerationMetadata>()?.SuppressLinkGeneration == true)
var suppressLinkGeneration = endpoint.Metadata.GetMetadata<ISuppressLinkGenerationMetadata>();
if (suppressLinkGeneration != null)
{ {
continue; continue;
} }

View File

@ -4,10 +4,14 @@
namespace Microsoft.AspNetCore.Routing namespace Microsoft.AspNetCore.Routing
{ {
/// <summary> /// <summary>
/// Represents metadata used during link generation. /// Represents metadata used during link generation. If <see cref="SuppressLinkGeneration"/> is <c>true</c>
/// The associated endpoint will not be considered for link generation. /// the associated endpoint will not be used for link generation.
/// </summary> /// </summary>
public sealed class SuppressLinkGenerationMetadata : ISuppressLinkGenerationMetadata public sealed class SuppressLinkGenerationMetadata : ISuppressLinkGenerationMetadata
{ {
/// <summary>
/// Gets a value indicating whether the assocated endpoint should be used for link generation.
/// </summary>
public bool SuppressLinkGeneration => true;
} }
} }

View File

@ -4,10 +4,14 @@
namespace Microsoft.AspNetCore.Routing namespace Microsoft.AspNetCore.Routing
{ {
/// <summary> /// <summary>
/// Metadata used to prevent URL matching. The associated endpoint will not be /// Metadata used to prevent URL matching. If <see cref="SuppressMatching"/> is <c>true</c> the
/// considered URL matching for incoming requests. /// associated endpoint will not be considered for URL matching.
/// </summary> /// </summary>
public sealed class SuppressMatchingMetadata : ISuppressMatchingMetadata public sealed class SuppressMatchingMetadata : ISuppressMatchingMetadata
{ {
/// <summary>
/// Gets a value indicating whether the assocated endpoint should be used for URL matching.
/// </summary>
public bool SuppressMatching => true;
} }
} }

View File

@ -599,14 +599,14 @@ namespace Microsoft.AspNetCore.Routing
protected override void AddAdditionalServices(IServiceCollection services) protected override void AddAdditionalServices(IServiceCollection services)
{ {
services.AddSingleton<IEndpointFinder<int>, IntEndpointFinder>(); services.AddSingleton<IEndpointAddressScheme<int>, IntAddressScheme>();
} }
private class IntEndpointFinder : IEndpointFinder<int> private class IntAddressScheme : IEndpointAddressScheme<int>
{ {
private readonly CompositeEndpointDataSource _dataSource; private readonly CompositeEndpointDataSource _dataSource;
public IntEndpointFinder(CompositeEndpointDataSource dataSource) public IntAddressScheme(CompositeEndpointDataSource dataSource)
{ {
_dataSource = dataSource; _dataSource = dataSource;
} }

View File

@ -9,10 +9,10 @@ using Xunit;
namespace Microsoft.AspNetCore.Routing namespace Microsoft.AspNetCore.Routing
{ {
public class EndpointNameEndpointFinderTest public class EndpointNameAddressSchemeTest
{ {
[Fact] [Fact]
public void EndpointFinder_Match_ReturnsMatchingEndpoint() public void AddressScheme_Match_ReturnsMatchingEndpoint()
{ {
// Arrange // Arrange
var endpoint1 = EndpointFactory.CreateRouteEndpoint( var endpoint1 = EndpointFactory.CreateRouteEndpoint(
@ -23,10 +23,10 @@ namespace Microsoft.AspNetCore.Routing
"/b", "/b",
metadata: new object[] { new EndpointNameMetadata("name2"), }); metadata: new object[] { new EndpointNameMetadata("name2"), });
var finder = CreateEndpointFinder(endpoint1, endpoint2); var addressScheme = CreateAddressScheme(endpoint1, endpoint2);
// Act // Act
var endpoints = finder.FindEndpoints("name2"); var endpoints = addressScheme.FindEndpoints("name2");
// Assert // Assert
Assert.Collection( Assert.Collection(
@ -35,41 +35,41 @@ namespace Microsoft.AspNetCore.Routing
} }
[Fact] [Fact]
public void EndpointFinder_NoMatch_ReturnsEmptyCollection() public void AddressScheme_NoMatch_ReturnsEmptyCollection()
{ {
// Arrange // Arrange
var endpoint = EndpointFactory.CreateRouteEndpoint( var endpoint = EndpointFactory.CreateRouteEndpoint(
"/a", "/a",
metadata: new object[] { new EndpointNameMetadata("name1"), new SuppressLinkGenerationMetadata(), }); metadata: new object[] { new EndpointNameMetadata("name1"), new SuppressLinkGenerationMetadata(), });
var finder = CreateEndpointFinder(endpoint); var addressScheme = CreateAddressScheme(endpoint);
// Act // Act
var endpoints = finder.FindEndpoints("name2"); var endpoints = addressScheme.FindEndpoints("name2");
// Assert // Assert
Assert.Empty(endpoints); Assert.Empty(endpoints);
} }
[Fact] [Fact]
public void EndpointFinder_NoMatch_CaseSensitive() public void AddressScheme_NoMatch_CaseSensitive()
{ {
// Arrange // Arrange
var endpoint = EndpointFactory.CreateRouteEndpoint( var endpoint = EndpointFactory.CreateRouteEndpoint(
"/a", "/a",
metadata: new object[] { new EndpointNameMetadata("name1"), new SuppressLinkGenerationMetadata(), }); metadata: new object[] { new EndpointNameMetadata("name1"), new SuppressLinkGenerationMetadata(), });
var finder = CreateEndpointFinder(endpoint); var addressScheme = CreateAddressScheme(endpoint);
// Act // Act
var endpoints = finder.FindEndpoints("NAME1"); var endpoints = addressScheme.FindEndpoints("NAME1");
// Assert // Assert
Assert.Empty(endpoints); Assert.Empty(endpoints);
} }
[Fact] [Fact]
public void EndpointFinder_UpdatesWhenDataSourceChanges() public void AddressScheme_UpdatesWhenDataSourceChanges()
{ {
var endpoint1 = EndpointFactory.CreateRouteEndpoint( var endpoint1 = EndpointFactory.CreateRouteEndpoint(
"/a", "/a",
@ -77,10 +77,10 @@ namespace Microsoft.AspNetCore.Routing
var dynamicDataSource = new DynamicEndpointDataSource(new[] { endpoint1 }); var dynamicDataSource = new DynamicEndpointDataSource(new[] { endpoint1 });
// Act 1 // Act 1
var finder = CreateEndpointFinder(dynamicDataSource); var addressScheme = CreateAddressScheme(dynamicDataSource);
// Assert 1 // Assert 1
var match = Assert.Single(finder.Entries); var match = Assert.Single(addressScheme.Entries);
Assert.Same(endpoint1, match.Value.Single()); Assert.Same(endpoint1, match.Value.Single());
// Arrange 2 // Arrange 2
@ -94,7 +94,7 @@ namespace Microsoft.AspNetCore.Routing
// Assert 2 // Assert 2
Assert.Collection( Assert.Collection(
finder.Entries.OrderBy(kvp => kvp.Key), addressScheme.Entries.OrderBy(kvp => kvp.Key),
(m) => (m) =>
{ {
Assert.Same(endpoint1, m.Value.Single()); Assert.Same(endpoint1, m.Value.Single());
@ -106,7 +106,7 @@ namespace Microsoft.AspNetCore.Routing
} }
[Fact] [Fact]
public void EndpointFinder_IgnoresEndpointsWithSuppressLinkGeneration() public void AddressScheme_IgnoresEndpointsWithSuppressLinkGeneration()
{ {
// Arrange // Arrange
var endpoint = EndpointFactory.CreateRouteEndpoint( var endpoint = EndpointFactory.CreateRouteEndpoint(
@ -114,14 +114,29 @@ namespace Microsoft.AspNetCore.Routing
metadata: new object[] { new EndpointNameMetadata("name1"), new SuppressLinkGenerationMetadata(), }); metadata: new object[] { new EndpointNameMetadata("name1"), new SuppressLinkGenerationMetadata(), });
// Act // Act
var finder = CreateEndpointFinder(endpoint); var addressScheme = CreateAddressScheme(endpoint);
// Assert // Assert
Assert.Empty(finder.Entries); Assert.Empty(addressScheme.Entries);
} }
[Fact] [Fact]
public void EndpointFinder_IgnoresEndpointsWithoutEndpointName() public void AddressScheme_UnsuppressedEndpoint_IsUsed()
{
// Arrange
var endpoint = EndpointFactory.CreateRouteEndpoint(
"/a",
metadata: new object[] { new EndpointNameMetadata("name1"), new SuppressLinkGenerationMetadata(), new EncourageLinkGenerationMetadata(), });
// Act
var addressScheme = CreateAddressScheme(endpoint);
// Assert
Assert.Same(endpoint, Assert.Single(Assert.Single(addressScheme.Entries).Value));
}
[Fact]
public void AddressScheme_IgnoresEndpointsWithoutEndpointName()
{ {
// Arrange // Arrange
var endpoint = EndpointFactory.CreateRouteEndpoint( var endpoint = EndpointFactory.CreateRouteEndpoint(
@ -129,14 +144,14 @@ namespace Microsoft.AspNetCore.Routing
metadata: new object[] { }); metadata: new object[] { });
// Act // Act
var finder = CreateEndpointFinder(endpoint); var addressScheme = CreateAddressScheme(endpoint);
// Assert // Assert
Assert.Empty(finder.Entries); Assert.Empty(addressScheme.Entries);
} }
[Fact] [Fact]
public void EndpointFinder_ThrowsExceptionForDuplicateEndpoints() public void AddressScheme_ThrowsExceptionForDuplicateEndpoints()
{ {
// Arrange // Arrange
var endpoints = new Endpoint[] var endpoints = new Endpoint[]
@ -152,10 +167,10 @@ namespace Microsoft.AspNetCore.Routing
EndpointFactory.CreateRouteEndpoint("/f", displayName: "f", metadata: new object[] { new EndpointNameMetadata("name2"), }), EndpointFactory.CreateRouteEndpoint("/f", displayName: "f", metadata: new object[] { new EndpointNameMetadata("name2"), }),
}; };
var finder = CreateEndpointFinder(endpoints); var addressScheme = CreateAddressScheme(endpoints);
// Act // Act
var ex = Assert.Throws<InvalidOperationException>(() => finder.FindEndpoints("any name")); var ex = Assert.Throws<InvalidOperationException>(() => addressScheme.FindEndpoints("any name"));
// Assert // Assert
Assert.Equal(@"The following endpoints with a duplicate endpoint name were found. Assert.Equal(@"The following endpoints with a duplicate endpoint name were found.
@ -171,14 +186,19 @@ f
", ex.Message); ", ex.Message);
} }
private EndpointNameEndpointFinder CreateEndpointFinder(params Endpoint[] endpoints) private EndpointNameAddressScheme CreateAddressScheme(params Endpoint[] endpoints)
{ {
return CreateEndpointFinder(new DefaultEndpointDataSource(endpoints)); return CreateAddressScheme(new DefaultEndpointDataSource(endpoints));
} }
private EndpointNameEndpointFinder CreateEndpointFinder(params EndpointDataSource[] dataSources) private EndpointNameAddressScheme CreateAddressScheme(params EndpointDataSource[] dataSources)
{ {
return new EndpointNameEndpointFinder(new CompositeEndpointDataSource(dataSources)); return new EndpointNameAddressScheme(new CompositeEndpointDataSource(dataSources));
}
private class EncourageLinkGenerationMetadata : ISuppressLinkGenerationMetadata
{
public bool SuppressLinkGeneration => false;
} }
} }
} }

View File

@ -14,7 +14,7 @@ namespace Microsoft.AspNetCore.Routing
// Does not cover template processing in detail, those scenarios are validated by TemplateBinderTests // Does not cover template processing in detail, those scenarios are validated by TemplateBinderTests
// and DefaultLinkGeneratorProcessTemplateTest // and DefaultLinkGeneratorProcessTemplateTest
// //
// Does not cover the EndpointNameEndpointFinder in detail. see EndpointNameEndpointFinderTest // Does not cover the EndpointNameAddressScheme in detail. see EndpointNameAddressSchemeTest
public class LinkGeneratorEndpointNameExtensionsTest : LinkGeneratorTestBase public class LinkGeneratorEndpointNameExtensionsTest : LinkGeneratorTestBase
{ {
[Fact] [Fact]

View File

@ -14,7 +14,7 @@ namespace Microsoft.AspNetCore.Routing
// Does not cover template processing in detail, those scenarios are validated by TemplateBinderTests // Does not cover template processing in detail, those scenarios are validated by TemplateBinderTests
// and DefaultLinkGeneratorProcessTemplateTest // and DefaultLinkGeneratorProcessTemplateTest
// //
// Does not cover the RouteValueBasedEndpointFinder in detail. see RouteValueBasedEndpointFinderTest // Does not cover the RouteValuesAddressScheme in detail. see RouteValuesAddressSchemeTest
public class LinkGeneratorRouteValuesAddressExtensionsTest : LinkGeneratorTestBase public class LinkGeneratorRouteValuesAddressExtensionsTest : LinkGeneratorTestBase
{ {
[Fact] [Fact]

View File

@ -5,7 +5,6 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Routing.Patterns; using Microsoft.AspNetCore.Routing.Patterns;
using Microsoft.AspNetCore.Routing.TestObjects; using Microsoft.AspNetCore.Routing.TestObjects;
using Xunit; using Xunit;
@ -89,6 +88,27 @@ namespace Microsoft.AspNetCore.Routing.Matching
Assert.Empty(inner.Endpoints); Assert.Empty(inner.Endpoints);
} }
[Fact]
public void Matcher_UnsuppressedEndpoint_IsUsed()
{
// Arrange
var dataSource = new DynamicEndpointDataSource();
var endpoint = new RouteEndpoint(
TestConstants.EmptyRequestDelegate,
RoutePatternFactory.Parse("/"),
0,
new EndpointMetadataCollection(new SuppressMatchingMetadata(), new EncourageMatchingMetadata()),
"test");
dataSource.AddEndpoint(endpoint);
// Act
var matcher = new DataSourceDependentMatcher(dataSource, TestMatcherBuilder.Create);
// Assert
var inner = Assert.IsType<TestMatcher>(matcher.CurrentMatcher);
Assert.Same(endpoint, Assert.Single(inner.Endpoints));
}
[Fact] [Fact]
public void Cache_Reinitializes_WhenDataSourceChanges() public void Cache_Reinitializes_WhenDataSourceChanges()
{ {
@ -139,5 +159,10 @@ namespace Microsoft.AspNetCore.Routing.Matching
throw new NotImplementedException(); throw new NotImplementedException();
} }
} }
private class EncourageMatchingMetadata : ISuppressMatchingMetadata
{
public bool SuppressMatching => false;
}
} }
} }

View File

@ -12,7 +12,7 @@ using Xunit;
namespace Microsoft.AspNetCore.Routing namespace Microsoft.AspNetCore.Routing
{ {
public class RouteValueBasedEndpointFinderTest public class RouteValuesAddressSchemeTest
{ {
[Fact] [Fact]
public void GetOutboundMatches_GetsNamedMatchesFor_EndpointsHaving_IRouteNameMetadata() public void GetOutboundMatches_GetsNamedMatchesFor_EndpointsHaving_IRouteNameMetadata()
@ -22,13 +22,13 @@ namespace Microsoft.AspNetCore.Routing
var endpoint2 = CreateEndpoint("/a", routeName: "named"); var endpoint2 = CreateEndpoint("/a", routeName: "named");
// Act // Act
var finder = CreateEndpointFinder(endpoint1, endpoint2); var addressScheme = CreateAddressScheme(endpoint1, endpoint2);
// Assert // Assert
Assert.NotNull(finder.AllMatches); Assert.NotNull(addressScheme.AllMatches);
Assert.Equal(2, finder.AllMatches.Count()); Assert.Equal(2, addressScheme.AllMatches.Count());
Assert.NotNull(finder.NamedMatches); Assert.NotNull(addressScheme.NamedMatches);
Assert.True(finder.NamedMatches.TryGetValue("named", out var namedMatches)); Assert.True(addressScheme.NamedMatches.TryGetValue("named", out var namedMatches));
var namedMatch = Assert.Single(namedMatches); var namedMatch = Assert.Single(namedMatches);
var actual = Assert.IsType<RouteEndpoint>(namedMatch.Match.Entry.Data); var actual = Assert.IsType<RouteEndpoint>(namedMatch.Match.Entry.Data);
Assert.Same(endpoint2, actual); Assert.Same(endpoint2, actual);
@ -43,13 +43,13 @@ namespace Microsoft.AspNetCore.Routing
var endpoint3 = CreateEndpoint("/b", routeName: "named"); var endpoint3 = CreateEndpoint("/b", routeName: "named");
// Act // Act
var finder = CreateEndpointFinder(endpoint1, endpoint2, endpoint3); var addressScheme = CreateAddressScheme(endpoint1, endpoint2, endpoint3);
// Assert // Assert
Assert.NotNull(finder.AllMatches); Assert.NotNull(addressScheme.AllMatches);
Assert.Equal(3, finder.AllMatches.Count()); Assert.Equal(3, addressScheme.AllMatches.Count());
Assert.NotNull(finder.NamedMatches); Assert.NotNull(addressScheme.NamedMatches);
Assert.True(finder.NamedMatches.TryGetValue("named", out var namedMatches)); Assert.True(addressScheme.NamedMatches.TryGetValue("named", out var namedMatches));
Assert.Equal(2, namedMatches.Count); Assert.Equal(2, namedMatches.Count);
Assert.Same(endpoint2, Assert.IsType<RouteEndpoint>(namedMatches[0].Match.Entry.Data)); Assert.Same(endpoint2, Assert.IsType<RouteEndpoint>(namedMatches[0].Match.Entry.Data));
Assert.Same(endpoint3, Assert.IsType<RouteEndpoint>(namedMatches[1].Match.Entry.Data)); Assert.Same(endpoint3, Assert.IsType<RouteEndpoint>(namedMatches[1].Match.Entry.Data));
@ -64,13 +64,13 @@ namespace Microsoft.AspNetCore.Routing
var endpoint3 = CreateEndpoint("/b", routeName: "NaMed"); var endpoint3 = CreateEndpoint("/b", routeName: "NaMed");
// Act // Act
var finder = CreateEndpointFinder(endpoint1, endpoint2, endpoint3); var addressScheme = CreateAddressScheme(endpoint1, endpoint2, endpoint3);
// Assert // Assert
Assert.NotNull(finder.AllMatches); Assert.NotNull(addressScheme.AllMatches);
Assert.Equal(3, finder.AllMatches.Count()); Assert.Equal(3, addressScheme.AllMatches.Count());
Assert.NotNull(finder.NamedMatches); Assert.NotNull(addressScheme.NamedMatches);
Assert.True(finder.NamedMatches.TryGetValue("named", out var namedMatches)); Assert.True(addressScheme.NamedMatches.TryGetValue("named", out var namedMatches));
Assert.Equal(2, namedMatches.Count); Assert.Equal(2, namedMatches.Count);
Assert.Same(endpoint2, Assert.IsType<RouteEndpoint>(namedMatches[0].Match.Entry.Data)); Assert.Same(endpoint2, Assert.IsType<RouteEndpoint>(namedMatches[0].Match.Entry.Data));
Assert.Same(endpoint3, Assert.IsType<RouteEndpoint>(namedMatches[1].Match.Entry.Data)); Assert.Same(endpoint3, Assert.IsType<RouteEndpoint>(namedMatches[1].Match.Entry.Data));
@ -84,11 +84,11 @@ namespace Microsoft.AspNetCore.Routing
var dynamicDataSource = new DynamicEndpointDataSource(new[] { endpoint1 }); var dynamicDataSource = new DynamicEndpointDataSource(new[] { endpoint1 });
// Act 1 // Act 1
var finder = new CustomRouteValuesBasedEndpointFinder(new CompositeEndpointDataSource(new[] { dynamicDataSource })); var addressScheme = new CustomRouteValuesBasedAddressScheme(new CompositeEndpointDataSource(new[] { dynamicDataSource }));
// Assert 1 // Assert 1
Assert.NotNull(finder.AllMatches); Assert.NotNull(addressScheme.AllMatches);
var match = Assert.Single(finder.AllMatches); var match = Assert.Single(addressScheme.AllMatches);
var actual = Assert.IsType<RouteEndpoint>(match.Entry.Data); var actual = Assert.IsType<RouteEndpoint>(match.Entry.Data);
Assert.Same(endpoint1, actual); Assert.Same(endpoint1, actual);
@ -114,9 +114,9 @@ namespace Microsoft.AspNetCore.Routing
dynamicDataSource.AddEndpoint(endpoint4); dynamicDataSource.AddEndpoint(endpoint4);
// Assert 3 // Assert 3
Assert.NotNull(finder.AllMatches); Assert.NotNull(addressScheme.AllMatches);
Assert.Collection( Assert.Collection(
finder.AllMatches, addressScheme.AllMatches,
(m) => (m) =>
{ {
actual = Assert.IsType<RouteEndpoint>(m.Entry.Data); actual = Assert.IsType<RouteEndpoint>(m.Entry.Data);
@ -148,10 +148,10 @@ namespace Microsoft.AspNetCore.Routing
defaults: new { controller = "Orders", action = "GetById" }, defaults: new { controller = "Orders", action = "GetById" },
requiredValues: new { controller = "Orders", action = "GetById" }, requiredValues: new { controller = "Orders", action = "GetById" },
routeName: "OrdersApi"); routeName: "OrdersApi");
var finder = CreateEndpointFinder(expected); var addressScheme = CreateAddressScheme(expected);
// Act // Act
var foundEndpoints = finder.FindEndpoints( var foundEndpoints = addressScheme.FindEndpoints(
new RouteValuesAddress new RouteValuesAddress
{ {
ExplicitValues = new RouteValueDictionary(new { id = 10 }), ExplicitValues = new RouteValueDictionary(new { id = 10 }),
@ -167,7 +167,7 @@ namespace Microsoft.AspNetCore.Routing
[Fact] [Fact]
public void FindEndpoints_AlwaysReturnsEndpointsByRouteName_IgnoringMissingRequiredParameterValues() public void FindEndpoints_AlwaysReturnsEndpointsByRouteName_IgnoringMissingRequiredParameterValues()
{ {
// Here 'id' is the required value. The endpoint finder would always return an endpoint by looking up // Here 'id' is the required value. The endpoint addressScheme would always return an endpoint by looking up
// name only. Its the link generator which uses these endpoints finally to generate a link or not // name only. Its the link generator which uses these endpoints finally to generate a link or not
// based on the required parameter values being present or not. // based on the required parameter values being present or not.
@ -177,10 +177,10 @@ namespace Microsoft.AspNetCore.Routing
defaults: new { controller = "Orders", action = "GetById" }, defaults: new { controller = "Orders", action = "GetById" },
requiredValues: new { controller = "Orders", action = "GetById" }, requiredValues: new { controller = "Orders", action = "GetById" },
routeName: "OrdersApi"); routeName: "OrdersApi");
var finder = CreateEndpointFinder(expected); var addressScheme = CreateAddressScheme(expected);
// Act // Act
var foundEndpoints = finder.FindEndpoints( var foundEndpoints = addressScheme.FindEndpoints(
new RouteValuesAddress new RouteValuesAddress
{ {
ExplicitValues = new RouteValueDictionary(), ExplicitValues = new RouteValueDictionary(),
@ -202,20 +202,35 @@ namespace Microsoft.AspNetCore.Routing
metadataCollection: new EndpointMetadataCollection(new[] { new SuppressLinkGenerationMetadata() })); metadataCollection: new EndpointMetadataCollection(new[] { new SuppressLinkGenerationMetadata() }));
// Act // Act
var finder = CreateEndpointFinder(endpoint); var addressScheme = CreateAddressScheme(endpoint);
// Assert // Assert
Assert.Empty(finder.AllMatches); Assert.Empty(addressScheme.AllMatches);
} }
private CustomRouteValuesBasedEndpointFinder CreateEndpointFinder(params Endpoint[] endpoints) [Fact]
public void AddressScheme_UnsuppressedEndpoint_IsUsed()
{ {
return CreateEndpointFinder(new DefaultEndpointDataSource(endpoints)); // Arrange
var endpoint = EndpointFactory.CreateRouteEndpoint(
"/a",
metadata: new object[] { new SuppressLinkGenerationMetadata(), new EncourageLinkGenerationMetadata(), });
// Act
var addressScheme = CreateAddressScheme(endpoint);
// Assert
Assert.Same(endpoint, Assert.Single(addressScheme.AllMatches).Entry.Data);
} }
private CustomRouteValuesBasedEndpointFinder CreateEndpointFinder(params EndpointDataSource[] dataSources) private CustomRouteValuesBasedAddressScheme CreateAddressScheme(params Endpoint[] endpoints)
{ {
return new CustomRouteValuesBasedEndpointFinder(new CompositeEndpointDataSource(dataSources)); return CreateAddressScheme(new DefaultEndpointDataSource(endpoints));
}
private CustomRouteValuesBasedAddressScheme CreateAddressScheme(params EndpointDataSource[] dataSources)
{
return new CustomRouteValuesBasedAddressScheme(new CompositeEndpointDataSource(dataSources));
} }
private RouteEndpoint CreateEndpoint( private RouteEndpoint CreateEndpoint(
@ -244,9 +259,9 @@ namespace Microsoft.AspNetCore.Routing
null); null);
} }
private class CustomRouteValuesBasedEndpointFinder : RouteValuesBasedEndpointFinder private class CustomRouteValuesBasedAddressScheme : RouteValuesAddressScheme
{ {
public CustomRouteValuesBasedEndpointFinder(CompositeEndpointDataSource dataSource) public CustomRouteValuesBasedAddressScheme(CompositeEndpointDataSource dataSource)
: base(dataSource) : base(dataSource)
{ {
} }
@ -263,5 +278,10 @@ namespace Microsoft.AspNetCore.Routing
return matches; return matches;
} }
} }
private class EncourageLinkGenerationMetadata : ISuppressLinkGenerationMetadata
{
public bool SuppressLinkGeneration => false;
}
} }
} }