diff --git a/src/Microsoft.AspNet.Routing/BuilderExtensions.cs b/src/Microsoft.AspNet.Routing/BuilderExtensions.cs index 4fabffefbe..748c621c86 100644 --- a/src/Microsoft.AspNet.Routing/BuilderExtensions.cs +++ b/src/Microsoft.AspNet.Routing/BuilderExtensions.cs @@ -3,6 +3,8 @@ using System; using Microsoft.AspNet.Routing; +using Microsoft.AspNet.Routing.Internal; +using Microsoft.Extensions.DependencyInjection; namespace Microsoft.AspNet.Builder { @@ -29,6 +31,14 @@ namespace Microsoft.AspNet.Builder throw new ArgumentNullException(nameof(router)); } + if (builder.ApplicationServices.GetService(typeof(RoutingMarkerService)) == null) + { + throw new InvalidOperationException(Resources.FormatUnableToFindServices( + nameof(IServiceCollection), + nameof(RoutingServiceCollectionExtensions.AddRouting), + "ConfigureServices(...)")); + } + return builder.UseMiddleware(router); } } diff --git a/src/Microsoft.AspNet.Routing/DependencyInjection/RoutingServiceCollectionExtensions.cs b/src/Microsoft.AspNet.Routing/DependencyInjection/RoutingServiceCollectionExtensions.cs index ee552f4724..7774071da4 100644 --- a/src/Microsoft.AspNet.Routing/DependencyInjection/RoutingServiceCollectionExtensions.cs +++ b/src/Microsoft.AspNet.Routing/DependencyInjection/RoutingServiceCollectionExtensions.cs @@ -34,6 +34,8 @@ namespace Microsoft.Extensions.DependencyInjection return provider.Create(new UriBuilderContextPooledObjectPolicy(encoder)); }); + services.TryAddSingleton(typeof(RoutingMarkerService)); + if (configureOptions != null) { services.Configure(configureOptions); diff --git a/src/Microsoft.AspNet.Routing/Internal/RoutingMarkerService.cs b/src/Microsoft.AspNet.Routing/Internal/RoutingMarkerService.cs new file mode 100644 index 0000000000..aa23baeebc --- /dev/null +++ b/src/Microsoft.AspNet.Routing/Internal/RoutingMarkerService.cs @@ -0,0 +1,15 @@ +// 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.Extensions.DependencyInjection; + +namespace Microsoft.AspNet.Routing.Internal +{ + /// + /// A marker class used to determine if all the routing services were added + /// to the before routing is configured. + /// + public class RoutingMarkerService + { + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Routing/Properties/Resources.Designer.cs b/src/Microsoft.AspNet.Routing/Properties/Resources.Designer.cs index 4bcd9e7d5e..bd68ed4e65 100644 --- a/src/Microsoft.AspNet.Routing/Properties/Resources.Designer.cs +++ b/src/Microsoft.AspNet.Routing/Properties/Resources.Designer.cs @@ -426,6 +426,22 @@ namespace Microsoft.AspNet.Routing return string.Format(CultureInfo.CurrentCulture, GetString("AttributeRoute_DifferentLinkGenerationEntries_SameName"), p0); } + /// + /// Unable to find the required services. Please add all the required services by calling '{0}.{1}' inside the call to '{2}' in the application startup code. + /// + internal static string UnableToFindServices + { + get { return GetString("UnableToFindServices"); } + } + + /// + /// Unable to find the required services. Please add all the required services by calling '{0}.{1}' inside the call to '{2}' in the application startup code. + /// + internal static string FormatUnableToFindServices(object p0, object p1, object p2) + { + return string.Format(CultureInfo.CurrentCulture, GetString("UnableToFindServices"), p0, p1, p2); + } + private static string GetString(string name, params string[] formatterNames) { var value = _resourceManager.GetString(name); diff --git a/src/Microsoft.AspNet.Routing/Resources.resx b/src/Microsoft.AspNet.Routing/Resources.resx index 1686265a63..97217dfa25 100644 --- a/src/Microsoft.AspNet.Routing/Resources.resx +++ b/src/Microsoft.AspNet.Routing/Resources.resx @@ -195,4 +195,7 @@ Two or more routes named '{0}' have different templates. + + Unable to find the required services. Please add all the required services by calling '{0}.{1}' inside the call to '{2}' in the application startup code. + \ No newline at end of file diff --git a/src/Microsoft.AspNet.Routing/RouteBuilder.cs b/src/Microsoft.AspNet.Routing/RouteBuilder.cs index 5e54be1037..6eb3476d47 100644 --- a/src/Microsoft.AspNet.Routing/RouteBuilder.cs +++ b/src/Microsoft.AspNet.Routing/RouteBuilder.cs @@ -4,6 +4,8 @@ using System; using System.Collections.Generic; using Microsoft.AspNet.Builder; +using Microsoft.AspNet.Routing.Internal; +using Microsoft.Extensions.DependencyInjection; namespace Microsoft.AspNet.Routing { @@ -21,6 +23,14 @@ namespace Microsoft.AspNet.Routing throw new ArgumentNullException(nameof(applicationBuilder)); } + if (applicationBuilder.ApplicationServices.GetService(typeof(RoutingMarkerService)) == null) + { + throw new InvalidOperationException(Resources.FormatUnableToFindServices( + nameof(IServiceCollection), + nameof(RoutingServiceCollectionExtensions.AddRouting), + "ConfigureServices(...)")); + } + ApplicationBuilder = applicationBuilder; ServiceProvider = applicationBuilder.ApplicationServices; diff --git a/test/Microsoft.AspNet.Routing.Tests/BuilderExtensionsTest.cs b/test/Microsoft.AspNet.Routing.Tests/BuilderExtensionsTest.cs new file mode 100644 index 0000000000..4546e880e6 --- /dev/null +++ b/test/Microsoft.AspNet.Routing.Tests/BuilderExtensionsTest.cs @@ -0,0 +1,35 @@ +// 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 Microsoft.AspNet.Routing; +using Moq; +using Xunit; + +namespace Microsoft.AspNet.Builder +{ + public class BuilderExtensionsTest + { + [Fact] + public void UseRouter_ThrowsInvalidOperationException_IfRoutingMarkerServiceIsNotRegistered() + { + // Arrange + var applicationBuilderMock = new Mock(); + applicationBuilderMock + .Setup(s => s.ApplicationServices) + .Returns(Mock.Of()); + + var router = Mock.Of(); + + // Act & Assert + var exception = Assert.Throws( + () => applicationBuilderMock.Object.UseRouter(router)); + + Assert.Equal( + "Unable to find the required services. Please add all the required services by calling " + + "'IServiceCollection.AddRouting' inside the call to 'ConfigureServices(...)'" + + " in the application startup code.", + exception.Message); + } + } +} diff --git a/test/Microsoft.AspNet.Routing.Tests/RouteBuilderTest.cs b/test/Microsoft.AspNet.Routing.Tests/RouteBuilderTest.cs new file mode 100644 index 0000000000..8813a0dbb9 --- /dev/null +++ b/test/Microsoft.AspNet.Routing.Tests/RouteBuilderTest.cs @@ -0,0 +1,32 @@ +// 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 Microsoft.AspNet.Builder; +using Moq; +using Xunit; + +namespace Microsoft.AspNet.Routing +{ + public class RouteBuilderTest + { + [Fact] + public void Ctor_ThrowsInvalidOperationException_IfRoutingMarkerServiceIsNotRegistered() + { + // Arrange + var applicationBuilderMock = new Mock(); + applicationBuilderMock + .Setup(s => s.ApplicationServices) + .Returns(Mock.Of()); + + // Act & Assert + var exception = Assert.Throws(() => new RouteBuilder(applicationBuilderMock.Object)); + + Assert.Equal( + "Unable to find the required services. Please add all the required services by calling " + + "'IServiceCollection.AddRouting' inside the call to 'ConfigureServices(...)'" + + " in the application startup code.", + exception.Message); + } + } +} diff --git a/test/Microsoft.AspNet.Routing.Tests/RouteTest.cs b/test/Microsoft.AspNet.Routing.Tests/RouteTest.cs index 09e020f92f..7f036544af 100644 --- a/test/Microsoft.AspNet.Routing.Tests/RouteTest.cs +++ b/test/Microsoft.AspNet.Routing.Tests/RouteTest.cs @@ -9,6 +9,7 @@ using Microsoft.AspNet.Builder; using Microsoft.AspNet.Http; using Microsoft.AspNet.Http.Internal; using Microsoft.AspNet.Routing.Constraints; +using Microsoft.AspNet.Routing.Internal; using Microsoft.AspNet.Testing; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; @@ -1578,6 +1579,7 @@ namespace Microsoft.AspNet.Routing { var services = new ServiceCollection(); services.AddSingleton(_inlineConstraintResolver); + services.AddSingleton(); var applicationBuilder = Mock.Of(); applicationBuilder.ApplicationServices = services.BuildServiceProvider(); diff --git a/test/Microsoft.AspNet.Routing.Tests/TemplateParserDefaultValuesTests.cs b/test/Microsoft.AspNet.Routing.Tests/TemplateParserDefaultValuesTests.cs index c7614038ee..4b1c97d8cd 100644 --- a/test/Microsoft.AspNet.Routing.Tests/TemplateParserDefaultValuesTests.cs +++ b/test/Microsoft.AspNet.Routing.Tests/TemplateParserDefaultValuesTests.cs @@ -3,6 +3,7 @@ using System; using Microsoft.AspNet.Builder; +using Microsoft.AspNet.Routing.Internal; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using Moq; @@ -112,6 +113,7 @@ namespace Microsoft.AspNet.Routing.Tests { var services = new ServiceCollection(); services.AddSingleton(_inlineConstraintResolver); + services.AddSingleton(); var applicationBuilder = Mock.Of(); applicationBuilder.ApplicationServices = services.BuildServiceProvider();