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

View File

@ -202,7 +202,7 @@ namespace Microsoft.AspNetCore.Routing
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();
if (endpoints.Count == 0)

View File

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

View File

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

View File

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

View File

@ -4,10 +4,14 @@
namespace Microsoft.AspNetCore.Routing
{
/// <summary>
/// Represents metadata used during link generation.
/// The associated endpoint will not be considered for link generation.
/// Represents metadata used during link generation. If <see cref="SuppressLinkGeneration"/> is <c>true</c>
/// the associated endpoint will not be used for link generation.
/// </summary>
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
{
/// <summary>
/// Metadata used to prevent URL matching. The associated endpoint will not be
/// considered URL matching for incoming requests.
/// Metadata used to prevent URL matching. If <see cref="SuppressMatching"/> is <c>true</c> the
/// associated endpoint will not be considered for URL matching.
/// </summary>
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;
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);
}

View File

@ -40,7 +40,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
// By design we only look at RouteEndpoint here. It's possible to
// register other endpoint types, which are non-routable, and it's
// 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);
}

View File

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

View File

@ -4,10 +4,14 @@
namespace Microsoft.AspNetCore.Routing
{
/// <summary>
/// Represents metadata used during link generation.
/// The associated endpoint will not be considered for link generation.
/// Represents metadata used during link generation. If <see cref="SuppressLinkGeneration"/> is <c>true</c>
/// the associated endpoint will not be used for link generation.
/// </summary>
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
{
/// <summary>
/// Metadata used to prevent URL matching. The associated endpoint will not be
/// considered URL matching for incoming requests.
/// Metadata used to prevent URL matching. If <see cref="SuppressMatching"/> is <c>true</c> the
/// associated endpoint will not be considered for URL matching.
/// </summary>
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)
{
services.AddSingleton<IEndpointFinder<int>, IntEndpointFinder>();
services.AddSingleton<IEndpointAddressScheme<int>, IntAddressScheme>();
}
private class IntEndpointFinder : IEndpointFinder<int>
private class IntAddressScheme : IEndpointAddressScheme<int>
{
private readonly CompositeEndpointDataSource _dataSource;
public IntEndpointFinder(CompositeEndpointDataSource dataSource)
public IntAddressScheme(CompositeEndpointDataSource dataSource)
{
_dataSource = dataSource;
}

View File

@ -9,10 +9,10 @@ using Xunit;
namespace Microsoft.AspNetCore.Routing
{
public class EndpointNameEndpointFinderTest
public class EndpointNameAddressSchemeTest
{
[Fact]
public void EndpointFinder_Match_ReturnsMatchingEndpoint()
public void AddressScheme_Match_ReturnsMatchingEndpoint()
{
// Arrange
var endpoint1 = EndpointFactory.CreateRouteEndpoint(
@ -23,10 +23,10 @@ namespace Microsoft.AspNetCore.Routing
"/b",
metadata: new object[] { new EndpointNameMetadata("name2"), });
var finder = CreateEndpointFinder(endpoint1, endpoint2);
var addressScheme = CreateAddressScheme(endpoint1, endpoint2);
// Act
var endpoints = finder.FindEndpoints("name2");
var endpoints = addressScheme.FindEndpoints("name2");
// Assert
Assert.Collection(
@ -35,41 +35,41 @@ namespace Microsoft.AspNetCore.Routing
}
[Fact]
public void EndpointFinder_NoMatch_ReturnsEmptyCollection()
public void AddressScheme_NoMatch_ReturnsEmptyCollection()
{
// Arrange
var endpoint = EndpointFactory.CreateRouteEndpoint(
"/a",
metadata: new object[] { new EndpointNameMetadata("name1"), new SuppressLinkGenerationMetadata(), });
var finder = CreateEndpointFinder(endpoint);
var addressScheme = CreateAddressScheme(endpoint);
// Act
var endpoints = finder.FindEndpoints("name2");
var endpoints = addressScheme.FindEndpoints("name2");
// Assert
Assert.Empty(endpoints);
}
[Fact]
public void EndpointFinder_NoMatch_CaseSensitive()
public void AddressScheme_NoMatch_CaseSensitive()
{
// Arrange
var endpoint = EndpointFactory.CreateRouteEndpoint(
"/a",
metadata: new object[] { new EndpointNameMetadata("name1"), new SuppressLinkGenerationMetadata(), });
var finder = CreateEndpointFinder(endpoint);
var addressScheme = CreateAddressScheme(endpoint);
// Act
var endpoints = finder.FindEndpoints("NAME1");
var endpoints = addressScheme.FindEndpoints("NAME1");
// Assert
Assert.Empty(endpoints);
}
[Fact]
public void EndpointFinder_UpdatesWhenDataSourceChanges()
public void AddressScheme_UpdatesWhenDataSourceChanges()
{
var endpoint1 = EndpointFactory.CreateRouteEndpoint(
"/a",
@ -77,10 +77,10 @@ namespace Microsoft.AspNetCore.Routing
var dynamicDataSource = new DynamicEndpointDataSource(new[] { endpoint1 });
// Act 1
var finder = CreateEndpointFinder(dynamicDataSource);
var addressScheme = CreateAddressScheme(dynamicDataSource);
// Assert 1
var match = Assert.Single(finder.Entries);
var match = Assert.Single(addressScheme.Entries);
Assert.Same(endpoint1, match.Value.Single());
// Arrange 2
@ -94,7 +94,7 @@ namespace Microsoft.AspNetCore.Routing
// Assert 2
Assert.Collection(
finder.Entries.OrderBy(kvp => kvp.Key),
addressScheme.Entries.OrderBy(kvp => kvp.Key),
(m) =>
{
Assert.Same(endpoint1, m.Value.Single());
@ -106,7 +106,7 @@ namespace Microsoft.AspNetCore.Routing
}
[Fact]
public void EndpointFinder_IgnoresEndpointsWithSuppressLinkGeneration()
public void AddressScheme_IgnoresEndpointsWithSuppressLinkGeneration()
{
// Arrange
var endpoint = EndpointFactory.CreateRouteEndpoint(
@ -114,14 +114,29 @@ namespace Microsoft.AspNetCore.Routing
metadata: new object[] { new EndpointNameMetadata("name1"), new SuppressLinkGenerationMetadata(), });
// Act
var finder = CreateEndpointFinder(endpoint);
var addressScheme = CreateAddressScheme(endpoint);
// Assert
Assert.Empty(finder.Entries);
Assert.Empty(addressScheme.Entries);
}
[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
var endpoint = EndpointFactory.CreateRouteEndpoint(
@ -129,14 +144,14 @@ namespace Microsoft.AspNetCore.Routing
metadata: new object[] { });
// Act
var finder = CreateEndpointFinder(endpoint);
var addressScheme = CreateAddressScheme(endpoint);
// Assert
Assert.Empty(finder.Entries);
Assert.Empty(addressScheme.Entries);
}
[Fact]
public void EndpointFinder_ThrowsExceptionForDuplicateEndpoints()
public void AddressScheme_ThrowsExceptionForDuplicateEndpoints()
{
// Arrange
var endpoints = new Endpoint[]
@ -152,10 +167,10 @@ namespace Microsoft.AspNetCore.Routing
EndpointFactory.CreateRouteEndpoint("/f", displayName: "f", metadata: new object[] { new EndpointNameMetadata("name2"), }),
};
var finder = CreateEndpointFinder(endpoints);
var addressScheme = CreateAddressScheme(endpoints);
// Act
var ex = Assert.Throws<InvalidOperationException>(() => finder.FindEndpoints("any name"));
var ex = Assert.Throws<InvalidOperationException>(() => addressScheme.FindEndpoints("any name"));
// Assert
Assert.Equal(@"The following endpoints with a duplicate endpoint name were found.
@ -171,14 +186,19 @@ f
", 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
// and DefaultLinkGeneratorProcessTemplateTest
//
// Does not cover the EndpointNameEndpointFinder in detail. see EndpointNameEndpointFinderTest
// Does not cover the EndpointNameAddressScheme in detail. see EndpointNameAddressSchemeTest
public class LinkGeneratorEndpointNameExtensionsTest : LinkGeneratorTestBase
{
[Fact]

View File

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

View File

@ -5,7 +5,6 @@ using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Routing.Patterns;
using Microsoft.AspNetCore.Routing.TestObjects;
using Xunit;
@ -89,6 +88,27 @@ namespace Microsoft.AspNetCore.Routing.Matching
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]
public void Cache_Reinitializes_WhenDataSourceChanges()
{
@ -139,5 +159,10 @@ namespace Microsoft.AspNetCore.Routing.Matching
throw new NotImplementedException();
}
}
private class EncourageMatchingMetadata : ISuppressMatchingMetadata
{
public bool SuppressMatching => false;
}
}
}

View File

@ -12,7 +12,7 @@ using Xunit;
namespace Microsoft.AspNetCore.Routing
{
public class RouteValueBasedEndpointFinderTest
public class RouteValuesAddressSchemeTest
{
[Fact]
public void GetOutboundMatches_GetsNamedMatchesFor_EndpointsHaving_IRouteNameMetadata()
@ -22,13 +22,13 @@ namespace Microsoft.AspNetCore.Routing
var endpoint2 = CreateEndpoint("/a", routeName: "named");
// Act
var finder = CreateEndpointFinder(endpoint1, endpoint2);
var addressScheme = CreateAddressScheme(endpoint1, endpoint2);
// Assert
Assert.NotNull(finder.AllMatches);
Assert.Equal(2, finder.AllMatches.Count());
Assert.NotNull(finder.NamedMatches);
Assert.True(finder.NamedMatches.TryGetValue("named", out var namedMatches));
Assert.NotNull(addressScheme.AllMatches);
Assert.Equal(2, addressScheme.AllMatches.Count());
Assert.NotNull(addressScheme.NamedMatches);
Assert.True(addressScheme.NamedMatches.TryGetValue("named", out var namedMatches));
var namedMatch = Assert.Single(namedMatches);
var actual = Assert.IsType<RouteEndpoint>(namedMatch.Match.Entry.Data);
Assert.Same(endpoint2, actual);
@ -43,13 +43,13 @@ namespace Microsoft.AspNetCore.Routing
var endpoint3 = CreateEndpoint("/b", routeName: "named");
// Act
var finder = CreateEndpointFinder(endpoint1, endpoint2, endpoint3);
var addressScheme = CreateAddressScheme(endpoint1, endpoint2, endpoint3);
// Assert
Assert.NotNull(finder.AllMatches);
Assert.Equal(3, finder.AllMatches.Count());
Assert.NotNull(finder.NamedMatches);
Assert.True(finder.NamedMatches.TryGetValue("named", out var namedMatches));
Assert.NotNull(addressScheme.AllMatches);
Assert.Equal(3, addressScheme.AllMatches.Count());
Assert.NotNull(addressScheme.NamedMatches);
Assert.True(addressScheme.NamedMatches.TryGetValue("named", out var namedMatches));
Assert.Equal(2, namedMatches.Count);
Assert.Same(endpoint2, Assert.IsType<RouteEndpoint>(namedMatches[0].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");
// Act
var finder = CreateEndpointFinder(endpoint1, endpoint2, endpoint3);
var addressScheme = CreateAddressScheme(endpoint1, endpoint2, endpoint3);
// Assert
Assert.NotNull(finder.AllMatches);
Assert.Equal(3, finder.AllMatches.Count());
Assert.NotNull(finder.NamedMatches);
Assert.True(finder.NamedMatches.TryGetValue("named", out var namedMatches));
Assert.NotNull(addressScheme.AllMatches);
Assert.Equal(3, addressScheme.AllMatches.Count());
Assert.NotNull(addressScheme.NamedMatches);
Assert.True(addressScheme.NamedMatches.TryGetValue("named", out var namedMatches));
Assert.Equal(2, namedMatches.Count);
Assert.Same(endpoint2, Assert.IsType<RouteEndpoint>(namedMatches[0].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 });
// Act 1
var finder = new CustomRouteValuesBasedEndpointFinder(new CompositeEndpointDataSource(new[] { dynamicDataSource }));
var addressScheme = new CustomRouteValuesBasedAddressScheme(new CompositeEndpointDataSource(new[] { dynamicDataSource }));
// Assert 1
Assert.NotNull(finder.AllMatches);
var match = Assert.Single(finder.AllMatches);
Assert.NotNull(addressScheme.AllMatches);
var match = Assert.Single(addressScheme.AllMatches);
var actual = Assert.IsType<RouteEndpoint>(match.Entry.Data);
Assert.Same(endpoint1, actual);
@ -114,9 +114,9 @@ namespace Microsoft.AspNetCore.Routing
dynamicDataSource.AddEndpoint(endpoint4);
// Assert 3
Assert.NotNull(finder.AllMatches);
Assert.NotNull(addressScheme.AllMatches);
Assert.Collection(
finder.AllMatches,
addressScheme.AllMatches,
(m) =>
{
actual = Assert.IsType<RouteEndpoint>(m.Entry.Data);
@ -148,10 +148,10 @@ namespace Microsoft.AspNetCore.Routing
defaults: new { controller = "Orders", action = "GetById" },
requiredValues: new { controller = "Orders", action = "GetById" },
routeName: "OrdersApi");
var finder = CreateEndpointFinder(expected);
var addressScheme = CreateAddressScheme(expected);
// Act
var foundEndpoints = finder.FindEndpoints(
var foundEndpoints = addressScheme.FindEndpoints(
new RouteValuesAddress
{
ExplicitValues = new RouteValueDictionary(new { id = 10 }),
@ -167,7 +167,7 @@ namespace Microsoft.AspNetCore.Routing
[Fact]
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
// based on the required parameter values being present or not.
@ -177,10 +177,10 @@ namespace Microsoft.AspNetCore.Routing
defaults: new { controller = "Orders", action = "GetById" },
requiredValues: new { controller = "Orders", action = "GetById" },
routeName: "OrdersApi");
var finder = CreateEndpointFinder(expected);
var addressScheme = CreateAddressScheme(expected);
// Act
var foundEndpoints = finder.FindEndpoints(
var foundEndpoints = addressScheme.FindEndpoints(
new RouteValuesAddress
{
ExplicitValues = new RouteValueDictionary(),
@ -202,20 +202,35 @@ namespace Microsoft.AspNetCore.Routing
metadataCollection: new EndpointMetadataCollection(new[] { new SuppressLinkGenerationMetadata() }));
// Act
var finder = CreateEndpointFinder(endpoint);
var addressScheme = CreateAddressScheme(endpoint);
// 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(
@ -244,9 +259,9 @@ namespace Microsoft.AspNetCore.Routing
null);
}
private class CustomRouteValuesBasedEndpointFinder : RouteValuesBasedEndpointFinder
private class CustomRouteValuesBasedAddressScheme : RouteValuesAddressScheme
{
public CustomRouteValuesBasedEndpointFinder(CompositeEndpointDataSource dataSource)
public CustomRouteValuesBasedAddressScheme(CompositeEndpointDataSource dataSource)
: base(dataSource)
{
}
@ -263,5 +278,10 @@ namespace Microsoft.AspNetCore.Routing
return matches;
}
}
private class EncourageLinkGenerationMetadata : ISuppressLinkGenerationMetadata
{
public bool SuppressLinkGeneration => false;
}
}
}