diff --git a/src/Microsoft.AspNetCore.Mvc.Razor/RazorPageActivator.cs b/src/Microsoft.AspNetCore.Mvc.Razor/RazorPageActivator.cs index fb9483856d..2bdcf10fcc 100644 --- a/src/Microsoft.AspNetCore.Mvc.Razor/RazorPageActivator.cs +++ b/src/Microsoft.AspNetCore.Mvc.Razor/RazorPageActivator.cs @@ -6,6 +6,7 @@ using System.Collections.Concurrent; using System.Diagnostics; using System.Linq.Expressions; using System.Reflection; +using System.Text.Encodings.Web; using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.Razor.Internal; using Microsoft.AspNetCore.Mvc.Rendering; @@ -29,13 +30,28 @@ namespace Microsoft.AspNetCore.Mvc.Razor private readonly ConcurrentDictionary _activationInfo; private readonly IModelMetadataProvider _metadataProvider; + // Value accessors for common singleton properties activated in a RazorPage. + private Func _urlHelperAccessor; + private Func _jsonHelperAccessor; + private Func _diagnosticSourceAccessor; + private Func _htmlEncoderAccessor; + /// /// Initializes a new instance of the class. /// - public RazorPageActivator(IModelMetadataProvider metadataProvider) + public RazorPageActivator( + IModelMetadataProvider metadataProvider, + IUrlHelperFactory urlHelperFactory, + IJsonHelper jsonHelper, + DiagnosticSource diagnosticSource, + HtmlEncoder htmlEncoder) { _activationInfo = new ConcurrentDictionary(); _metadataProvider = metadataProvider; + _urlHelperAccessor = context => urlHelperFactory.GetUrlHelper(context); + _jsonHelperAccessor = context => jsonHelper; + _diagnosticSourceAccessor = context => diagnosticSource; + _htmlEncoderAccessor = context => htmlEncoder; } /// @@ -160,12 +176,19 @@ namespace Microsoft.AspNetCore.Mvc.Razor // W.r.t. specificity of above condition: Users are much more likely to inject their own // IUrlHelperFactory than to create a class implementing IUrlHelper (or a sub-interface) and inject // that. But the second scenario is supported. (Note the class must implement ICanHasViewContext.) - valueAccessor = context => - { - var serviceProvider = context.HttpContext.RequestServices; - var factory = serviceProvider.GetRequiredService(); - return factory.GetUrlHelper(context); - }; + valueAccessor = _urlHelperAccessor; + } + else if (property.PropertyType == typeof(IJsonHelper)) + { + valueAccessor = _jsonHelperAccessor; + } + else if (property.PropertyType == typeof(DiagnosticSource)) + { + valueAccessor = _diagnosticSourceAccessor; + } + else if (property.PropertyType == typeof(HtmlEncoder)) + { + valueAccessor = _htmlEncoderAccessor; } else { diff --git a/test/Microsoft.AspNetCore.Mvc.Razor.Test/RazorPageActivatorTest.cs b/test/Microsoft.AspNetCore.Mvc.Razor.Test/RazorPageActivatorTest.cs index dab084c91e..a9b80a25c3 100644 --- a/test/Microsoft.AspNetCore.Mvc.Razor.Test/RazorPageActivatorTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.Razor.Test/RazorPageActivatorTest.cs @@ -4,10 +4,10 @@ using System; using System.Diagnostics; using System.IO; -using System.Text.Encodings.Web; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.Abstractions; +using Microsoft.AspNetCore.Mvc.Formatters; using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.Razor.Internal; using Microsoft.AspNetCore.Mvc.Rendering; @@ -29,19 +29,26 @@ namespace Microsoft.AspNetCore.Mvc.Razor public void Activate_ActivatesAndContextualizesPropertiesOnViews() { // Arrange - var activator = new RazorPageActivator(new EmptyModelMetadataProvider()); + var urlHelperFactory = new UrlHelperFactory(); + var jsonHelper = new JsonHelper(new JsonOutputFormatter()); + var htmlEncoder = new HtmlTestEncoder(); + var diagnosticSource = new DiagnosticListener("Microsoft.AspNetCore"); + var activator = new RazorPageActivator( + new EmptyModelMetadataProvider(), + urlHelperFactory, + jsonHelper, + diagnosticSource, + htmlEncoder); + var instance = new TestRazorPage(); var myService = new MyService(); var helper = Mock.Of>(); - var htmlEncoder = new HtmlTestEncoder(); - var diagnosticSource = new DiagnosticListener("Microsoft.AspNetCore"); + var serviceProvider = new ServiceCollection() .AddSingleton(myService) .AddSingleton(helper) - .AddSingleton(htmlEncoder) .AddSingleton(new ExpressionTextCache()) - .AddSingleton(diagnosticSource) .BuildServiceProvider(); var httpContext = new DefaultHttpContext { @@ -57,6 +64,8 @@ namespace Microsoft.AspNetCore.Mvc.Razor TextWriter.Null, new HtmlHelperOptions()); + var urlHelper = urlHelperFactory.GetUrlHelper(viewContext); + // Act activator.Activate(instance, viewContext); @@ -65,6 +74,9 @@ namespace Microsoft.AspNetCore.Mvc.Razor Assert.Same(myService, instance.MyService); Assert.Same(viewContext, myService.ViewContext); Assert.Same(diagnosticSource, instance.DiagnosticSource); + Assert.Same(htmlEncoder, instance.HtmlEncoder); + Assert.Same(jsonHelper, instance.Json); + Assert.Same(urlHelper, instance.Url); Assert.Null(instance.MyService2); } @@ -72,7 +84,13 @@ namespace Microsoft.AspNetCore.Mvc.Razor public void Activate_ThrowsIfTheViewDoesNotDeriveFromRazorViewOfT() { // Arrange - var activator = new RazorPageActivator(new EmptyModelMetadataProvider()); + var activator = new RazorPageActivator( + new EmptyModelMetadataProvider(), + new UrlHelperFactory(), + new JsonHelper(new JsonOutputFormatter()), + new DiagnosticListener("Microsoft.AspNetCore"), + new HtmlTestEncoder()); + var instance = new DoesNotDeriveFromRazorPageOfT(); var myService = new MyService(); @@ -102,18 +120,20 @@ namespace Microsoft.AspNetCore.Mvc.Razor public void Activate_InstantiatesNewViewDataDictionaryType_IfTheTypeDoesNotMatch() { // Arrange - var activator = new RazorPageActivator(new EmptyModelMetadataProvider()); + var activator = new RazorPageActivator( + new EmptyModelMetadataProvider(), + new UrlHelperFactory(), + new JsonHelper(new JsonOutputFormatter()), + new DiagnosticListener("Microsoft.AspNetCore.Mvc"), + new HtmlTestEncoder()); var instance = new TestRazorPage(); var myService = new MyService(); var helper = Mock.Of>(); - var htmlEncoder = new HtmlTestEncoder(); var serviceProvider = new ServiceCollection() .AddSingleton(myService) .AddSingleton(helper) - .AddSingleton(htmlEncoder) .AddSingleton(new ExpressionTextCache()) - .AddSingleton(new DiagnosticListener("Microsoft.AspNetCore.Mvc")) .BuildServiceProvider(); var httpContext = new DefaultHttpContext { @@ -144,17 +164,19 @@ namespace Microsoft.AspNetCore.Mvc.Razor public void Activate_UsesPassedInViewDataDictionaryInstance_IfPassedInTypeMatches() { // Arrange - var activator = new RazorPageActivator(new EmptyModelMetadataProvider()); + var activator = new RazorPageActivator( + new EmptyModelMetadataProvider(), + new UrlHelperFactory(), + new JsonHelper(new JsonOutputFormatter()), + new DiagnosticListener("Microsoft.AspNetCore.Mvc"), + new HtmlTestEncoder()); var instance = new TestRazorPage(); var myService = new MyService(); var helper = Mock.Of>(); - var htmlEncoder = new HtmlTestEncoder(); var serviceProvider = new ServiceCollection() .AddSingleton(myService) .AddSingleton(helper) - .AddSingleton(htmlEncoder) .AddSingleton(new ExpressionTextCache()) - .AddSingleton(new DiagnosticListener("Microsoft.AspNetCore.Mvc")) .BuildServiceProvider(); var httpContext = new DefaultHttpContext { @@ -185,17 +207,19 @@ namespace Microsoft.AspNetCore.Mvc.Razor public void Activate_DeterminesModelTypeFromProperty() { // Arrange - var activator = new RazorPageActivator(new EmptyModelMetadataProvider()); + var activator = new RazorPageActivator( + new EmptyModelMetadataProvider(), + new UrlHelperFactory(), + new JsonHelper(new JsonOutputFormatter()), + new DiagnosticListener("Microsoft.AspNetCore.Mvc"), + new HtmlTestEncoder()); var instance = new DoesNotDeriveFromRazorPageOfTButHasModelProperty(); var myService = new MyService(); var helper = Mock.Of>(); - var htmlEncoder = new HtmlTestEncoder(); var serviceProvider = new ServiceCollection() .AddSingleton(myService) .AddSingleton(helper) - .AddSingleton(htmlEncoder) .AddSingleton(new ExpressionTextCache()) - .AddSingleton(new DiagnosticListener("Microsoft.AspNetCore.Mvc")) .BuildServiceProvider(); var httpContext = new DefaultHttpContext { @@ -223,14 +247,16 @@ namespace Microsoft.AspNetCore.Mvc.Razor public void Activate_Throws_WhenViewDataPropertyHasIncorrectType() { // Arrange - var activator = new RazorPageActivator(new EmptyModelMetadataProvider()); + var activator = new RazorPageActivator( + new EmptyModelMetadataProvider(), + new UrlHelperFactory(), + new JsonHelper(new JsonOutputFormatter()), + new DiagnosticListener("Microsoft.AspNetCore.Mvc"), + new HtmlTestEncoder()); var instance = new HasIncorrectViewDataPropertyType(); var collection = new ServiceCollection(); - collection - .AddSingleton(new HtmlTestEncoder()) - .AddSingleton(new ExpressionTextCache()) - .AddSingleton(new DiagnosticListener("Microsoft.AspNetCore.Mvc")); + collection.AddSingleton(new ExpressionTextCache()); var httpContext = new DefaultHttpContext { RequestServices = collection.BuildServiceProvider(), @@ -253,15 +279,17 @@ namespace Microsoft.AspNetCore.Mvc.Razor public void Activate_CanGetUrlHelperFromDependencyInjection() { // Arrange - var activator = new RazorPageActivator(new EmptyModelMetadataProvider()); + var activator = new RazorPageActivator( + new EmptyModelMetadataProvider(), + new UrlHelperFactory(), + new JsonHelper(new JsonOutputFormatter()), + new DiagnosticListener("Microsoft.AspNetCore.Mvc"), + new HtmlTestEncoder()); var instance = new HasUnusualIUrlHelperProperty(); // IUrlHelperFactory should not be used. But set it up to match a real configuration. var collection = new ServiceCollection(); collection - .AddSingleton() - .AddSingleton(new HtmlTestEncoder()) - .AddSingleton(new DiagnosticListener("Microsoft.AspNetCore.Mvc")) .AddSingleton(new ExpressionTextCache()) .AddSingleton(); var httpContext = new DefaultHttpContext @@ -291,6 +319,12 @@ namespace Microsoft.AspNetCore.Mvc.Razor public MyService MyService { get; set; } public MyService MyService2 { get; set; } + + [RazorInject] + public IJsonHelper Json { get; set; } + + [RazorInject] + public IUrlHelper Url { get; set; } } private class TestRazorPage : TestPageBase diff --git a/test/Microsoft.AspNetCore.Mvc.Razor.Test/RazorPageCreateTagHelperTest.cs b/test/Microsoft.AspNetCore.Mvc.Razor.Test/RazorPageCreateTagHelperTest.cs index 12438a59a0..9ed8b0484b 100644 --- a/test/Microsoft.AspNetCore.Mvc.Razor.Test/RazorPageCreateTagHelperTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.Razor.Test/RazorPageCreateTagHelperTest.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; @@ -14,10 +15,12 @@ using Microsoft.AspNetCore.Mvc.Internal; using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.Razor.Internal; using Microsoft.AspNetCore.Mvc.Rendering; +using Microsoft.AspNetCore.Mvc.Routing; using Microsoft.AspNetCore.Mvc.ViewEngines; using Microsoft.AspNetCore.Mvc.ViewFeatures; using Microsoft.AspNetCore.Razor.TagHelpers; using Microsoft.AspNetCore.Routing; +using Microsoft.Extensions.WebEncoders.Testing; using Moq; using Xunit; @@ -66,7 +69,13 @@ namespace Microsoft.AspNetCore.Mvc.Razor private static TestRazorPage CreateTestRazorPage() { - var activator = new RazorPageActivator(new EmptyModelMetadataProvider()); + var activator = new RazorPageActivator( + new EmptyModelMetadataProvider(), + new UrlHelperFactory(), + new JsonHelper(new Formatters.JsonOutputFormatter()), + new DiagnosticListener("Microsoft.AspNetCore"), + new HtmlTestEncoder()); + var serviceProvider = new Mock(); var typeActivator = new TypeActivatorCache(); var tagHelperActivator = new DefaultTagHelperActivator(typeActivator); diff --git a/test/Microsoft.AspNetCore.Mvc.Test/MvcOptionsSetupTest.cs b/test/Microsoft.AspNetCore.Mvc.Test/MvcOptionsSetupTest.cs index 4ae1d69223..de12b9bed4 100644 --- a/test/Microsoft.AspNetCore.Mvc.Test/MvcOptionsSetupTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.Test/MvcOptionsSetupTest.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Diagnostics; using System.Linq; using System.Reflection; using System.Threading; @@ -240,6 +241,7 @@ namespace Microsoft.AspNetCore.Mvc { var serviceCollection = new ServiceCollection(); serviceCollection.AddSingleton(new ApplicationPartManager()); + serviceCollection.AddSingleton(new DiagnosticListener("Microsoft.AspNetCore.Mvc")); serviceCollection.AddMvc(); serviceCollection .AddSingleton()