Register DependencyContextRazorViewEngineOptionsSetup after RazorViewEngineOptionsSetup

Fixes #4902
This commit is contained in:
Pranav K 2016-07-07 16:44:54 -07:00
parent 8ec27958e7
commit b7a0393311
5 changed files with 346 additions and 20 deletions

View File

@ -127,18 +127,20 @@ namespace Microsoft.Extensions.DependencyInjection
// Internal for testing.
internal static void AddRazorViewEngineServices(IServiceCollection services)
{
services.TryAddEnumerable(
ServiceDescriptor.Transient<
IConfigureOptions<RazorViewEngineOptions>,
DependencyContextRazorViewEngineOptionsSetup>());
// This caches compilation related details that are valid across the lifetime of the application.
services.TryAddSingleton<ICompilationService, DefaultRoslynCompilationService>();
services.TryAddEnumerable(
ServiceDescriptor.Transient<IConfigureOptions<MvcViewOptions>, MvcRazorMvcViewOptionsSetup>());
// DependencyContextRazorViewEngineOptionsSetup needs to run after RazorViewEngineOptionsSetup.
// The ordering of the following two lines is important to ensure this behavior.
services.TryAddEnumerable(
ServiceDescriptor.Transient<IConfigureOptions<RazorViewEngineOptions>, RazorViewEngineOptionsSetup>());
services.TryAddEnumerable(
ServiceDescriptor.Transient<
IConfigureOptions<RazorViewEngineOptions>,
DependencyContextRazorViewEngineOptionsSetup>());
services.TryAddSingleton<
IRazorViewEngineFileProviderAccessor,

View File

@ -10,6 +10,7 @@ using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.Extensions.DependencyModel;
using Microsoft.Extensions.Options;
using DependencyContextOptions = Microsoft.Extensions.DependencyModel.CompilationOptions;
namespace Microsoft.AspNetCore.Mvc.Razor.Internal
{
@ -17,27 +18,46 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
/// Sets up compilation and parse option default options for <see cref="RazorViewEngineOptions"/> using
/// <see cref="DependencyContext"/>
/// </summary>
public class DependencyContextRazorViewEngineOptionsSetup : ConfigureOptions<RazorViewEngineOptions>
public class DependencyContextRazorViewEngineOptionsSetup : IConfigureOptions<RazorViewEngineOptions>
{
private readonly IHostingEnvironment _hostingEnvironment;
/// <summary>
/// Initializes a new instance of <see cref="DependencyContextRazorViewEngineOptionsSetup"/>.
/// </summary>
public DependencyContextRazorViewEngineOptionsSetup(IHostingEnvironment hostingEnvironment)
: base(options => ConfigureRazor(options, hostingEnvironment))
{
_hostingEnvironment = hostingEnvironment;
}
private static void ConfigureRazor(RazorViewEngineOptions options, IHostingEnvironment hostingEnvironment)
/// <inheritdoc />
public void Configure(RazorViewEngineOptions options)
{
var applicationAssembly = Assembly.Load(new AssemblyName(hostingEnvironment.ApplicationName));
var dependencyContext = DependencyContext.Load(applicationAssembly);
var compilationOptions = dependencyContext?.CompilationOptions ?? Extensions.DependencyModel.CompilationOptions.Default;
var compilationOptions = GetCompilationOptions();
SetParseOptions(options, compilationOptions);
SetCompilationOptions(options, compilationOptions);
}
private static void SetCompilationOptions(RazorViewEngineOptions options, Extensions.DependencyModel.CompilationOptions compilationOptions)
// Internal for unit testing.
protected internal virtual DependencyContextOptions GetCompilationOptions()
{
if (!string.IsNullOrEmpty(_hostingEnvironment.ApplicationName))
{
var applicationAssembly = Assembly.Load(new AssemblyName(_hostingEnvironment.ApplicationName));
var dependencyContext = DependencyContext.Load(applicationAssembly);
if (dependencyContext?.CompilationOptions != null)
{
return dependencyContext.CompilationOptions;
}
}
return DependencyContextOptions.Default;
}
private static void SetCompilationOptions(
RazorViewEngineOptions options,
DependencyContextOptions compilationOptions)
{
var roslynOptions = options.CompilationOptions;
@ -57,17 +77,17 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
if (compilationOptions.Optimize.HasValue)
{
var optimizationLevel = compilationOptions.Optimize.Value
? OptimizationLevel.Debug
: OptimizationLevel.Release;
var optimizationLevel = compilationOptions.Optimize.Value ?
OptimizationLevel.Release :
OptimizationLevel.Debug;
roslynOptions = roslynOptions.WithOptimizationLevel(optimizationLevel);
}
if (compilationOptions.WarningsAsErrors.HasValue)
{
var reportDiagnostic = compilationOptions.WarningsAsErrors.Value
? ReportDiagnostic.Error
: ReportDiagnostic.Default;
var reportDiagnostic = compilationOptions.WarningsAsErrors.Value ?
ReportDiagnostic.Error :
ReportDiagnostic.Default;
roslynOptions = roslynOptions.WithGeneralDiagnosticOption(reportDiagnostic);
}
@ -76,7 +96,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
private static void SetParseOptions(
RazorViewEngineOptions options,
Extensions.DependencyModel.CompilationOptions compilationOptions)
DependencyContextOptions compilationOptions)
{
var parseOptions = options.ParseOptions;
parseOptions = parseOptions.WithPreprocessorSymbols(

View File

@ -0,0 +1,301 @@
// 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.Linq;
using System.Reflection;
using Microsoft.AspNetCore.Hosting;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Moq;
using Xunit;
using DependencyContextOptions = Microsoft.Extensions.DependencyModel.CompilationOptions;
namespace Microsoft.AspNetCore.Mvc.Razor.Internal
{
public class DependencyContextRazorViewEngineOptionsSetupTest
{
[Theory]
[InlineData(null)]
[InlineData("")]
public void GetCompilationOptions_ReturnsDefaultOptionsIfApplicationNameIsNullOrEmpty(string name)
{
// Arrange
var hostingEnvironment = new Mock<IHostingEnvironment>();
hostingEnvironment.SetupGet(e => e.ApplicationName)
.Returns(name);
var setup = new TestableDependencyContextOptionsSetup(hostingEnvironment.Object);
// Act
var options = setup.GetCompilationOptionsPublic();
// Assert
Assert.Same(DependencyContextOptions.Default, options);
}
[Fact]
public void GetCompilationOptions_ReturnsDefaultOptionsIfApplicationDoesNotHaveDependencyContext()
{
// Arrange
var hostingEnvironment = new Mock<IHostingEnvironment>();
hostingEnvironment.SetupGet(e => e.ApplicationName)
.Returns(typeof(Controller).GetTypeInfo().Assembly.GetName().Name);
var setup = new TestableDependencyContextOptionsSetup(hostingEnvironment.Object);
// Act
var options = setup.GetCompilationOptionsPublic();
// Assert
Assert.Same(DependencyContextOptions.Default, options);
}
[Fact]
public void GetCompilationOptions_ReturnsCompilationOptionsFromDependencyContext()
{
// Arrange
var hostingEnvironment = new Mock<IHostingEnvironment>();
hostingEnvironment.SetupGet(e => e.ApplicationName)
.Returns(GetType().GetTypeInfo().Assembly.GetName().Name);
var setup = new TestableDependencyContextOptionsSetup(hostingEnvironment.Object);
// Act
var options = setup.GetCompilationOptionsPublic();
// Assert
Assert.Contains("SOME_TEST_DEFINE", options.Defines);
}
[Fact]
public void Configure_UsesDefaultCompilationOptions()
{
// Arrange
var hostingEnvironment = new Mock<IHostingEnvironment>();
var setup = new DependencyContextRazorViewEngineOptionsSetup(hostingEnvironment.Object);
var options = new RazorViewEngineOptions();
// Act
setup.Configure(options);
// Assert
var compilationOptions = options.CompilationOptions;
var parseOptions = options.ParseOptions;
Assert.False(compilationOptions.AllowUnsafe);
Assert.Equal(ReportDiagnostic.Default, compilationOptions.GeneralDiagnosticOption);
Assert.Equal(OptimizationLevel.Debug, compilationOptions.OptimizationLevel);
Assert.Collection(compilationOptions.SpecificDiagnosticOptions.OrderBy(d => d.Key),
item =>
{
Assert.Equal("CS1701", item.Key);
Assert.Equal(ReportDiagnostic.Suppress, item.Value);
},
item =>
{
Assert.Equal("CS1702", item.Key);
Assert.Equal(ReportDiagnostic.Suppress, item.Value);
},
item =>
{
Assert.Equal("CS1705", item.Key);
Assert.Equal(ReportDiagnostic.Suppress, item.Value);
});
Assert.Empty(parseOptions.PreprocessorSymbolNames);
Assert.Equal(LanguageVersion.CSharp6, parseOptions.LanguageVersion);
}
[Fact]
public void Configure_SetsAllowUnsafe()
{
// Arrange
var dependencyContextOptions = new DependencyContextOptions(
new[] { "MyDefine" },
languageVersion: null,
platform: null,
allowUnsafe: true,
warningsAsErrors: null,
optimize: null,
keyFile: null,
delaySign: null,
publicSign: null,
debugType: null,
emitEntryPoint: null,
generateXmlDocumentation: null);
var setup = new TestableDependencyContextOptionsSetup(dependencyContextOptions);
var options = new RazorViewEngineOptions();
// Act
setup.Configure(options);
// Assert
Assert.True(options.CompilationOptions.AllowUnsafe);
Assert.Equal(ReportDiagnostic.Default, options.CompilationOptions.GeneralDiagnosticOption);
Assert.Equal(OptimizationLevel.Debug, options.CompilationOptions.OptimizationLevel);
}
[Fact]
public void Configure_SetsDiagnosticOption()
{
// Arrange
var dependencyContextOptions = new DependencyContextOptions(
new[] { "MyDefine" },
languageVersion: null,
platform: null,
allowUnsafe: null,
warningsAsErrors: true,
optimize: null,
keyFile: null,
delaySign: null,
publicSign: null,
debugType: null,
emitEntryPoint: null,
generateXmlDocumentation: null);
var setup = new TestableDependencyContextOptionsSetup(dependencyContextOptions);
var options = new RazorViewEngineOptions();
// Act
setup.Configure(options);
// Assert
Assert.False(options.CompilationOptions.AllowUnsafe);
Assert.Equal(ReportDiagnostic.Error, options.CompilationOptions.GeneralDiagnosticOption);
Assert.Equal(OptimizationLevel.Debug, options.CompilationOptions.OptimizationLevel);
}
[Fact]
public void Configure_SetsOptimizationLevel()
{
// Arrange
var dependencyContextOptions = new DependencyContextOptions(
new[] { "MyDefine" },
languageVersion: null,
platform: null,
allowUnsafe: null,
warningsAsErrors: null,
optimize: true,
keyFile: null,
delaySign: null,
publicSign: null,
debugType: null,
emitEntryPoint: null,
generateXmlDocumentation: null);
var setup = new TestableDependencyContextOptionsSetup(dependencyContextOptions);
var options = new RazorViewEngineOptions();
// Act
setup.Configure(options);
// Assert
Assert.False(options.CompilationOptions.AllowUnsafe);
Assert.Equal(ReportDiagnostic.Default, options.CompilationOptions.GeneralDiagnosticOption);
Assert.Equal(OptimizationLevel.Release, options.CompilationOptions.OptimizationLevel);
}
[Fact]
public void Configure_SetsLanguageVersion()
{
// Arrange
var dependencyContextOptions = new DependencyContextOptions(
new[] { "MyDefine" },
languageVersion: "csharp4",
platform: null,
allowUnsafe: null,
warningsAsErrors: null,
optimize: true,
keyFile: null,
delaySign: null,
publicSign: null,
debugType: null,
emitEntryPoint: null,
generateXmlDocumentation: null);
var setup = new TestableDependencyContextOptionsSetup(dependencyContextOptions);
var options = new RazorViewEngineOptions();
// Act
setup.Configure(options);
// Assert
Assert.Equal(LanguageVersion.CSharp4, options.ParseOptions.LanguageVersion);
}
[Fact]
public void Configure_SetsDefines()
{
// Arrange
var dependencyContextOptions = new DependencyContextOptions(
new[] { "MyDefine" },
languageVersion: "csharp4",
platform: null,
allowUnsafe: null,
warningsAsErrors: null,
optimize: true,
keyFile: null,
delaySign: null,
publicSign: null,
debugType: null,
emitEntryPoint: null,
generateXmlDocumentation: null);
var setup = new TestableDependencyContextOptionsSetup(dependencyContextOptions);
var options = new RazorViewEngineOptions();
// Act
setup.Configure(options);
// Assert
Assert.Equal(new[] { "MyDefine" }, options.ParseOptions.PreprocessorSymbolNames);
}
[Fact]
public void ConfigureAfterRazorViewEngineOptionsSetupIsExecuted_CorrectlySetsUpOptimizationLevel()
{
// Arrange
var dependencyContextOptions = new DependencyContextOptions(
new[] { "MyDefine" },
languageVersion: null,
platform: null,
allowUnsafe: null,
warningsAsErrors: null,
optimize: true,
keyFile: null,
delaySign: null,
publicSign: null,
debugType: null,
emitEntryPoint: null,
generateXmlDocumentation: null);
var dependencyContextSetup = new TestableDependencyContextOptionsSetup(dependencyContextOptions);
var options = new RazorViewEngineOptions();
var hostingEnvironment = new Mock<IHostingEnvironment>();
hostingEnvironment.SetupGet(e => e.EnvironmentName)
.Returns("Development");
var viewEngineSetup = new RazorViewEngineOptionsSetup(hostingEnvironment.Object);
// Act
viewEngineSetup.Configure(options);
dependencyContextSetup.Configure(options);
// Assert
Assert.Equal(OptimizationLevel.Release, options.CompilationOptions.OptimizationLevel);
}
private class TestableDependencyContextOptionsSetup : DependencyContextRazorViewEngineOptionsSetup
{
private readonly DependencyContextOptions _options;
public TestableDependencyContextOptionsSetup(IHostingEnvironment hostingEnvironment)
: base(hostingEnvironment)
{
}
public TestableDependencyContextOptionsSetup(DependencyContextOptions options)
: base(Mock.Of<IHostingEnvironment>())
{
_options = options;
}
protected internal override DependencyContextOptions GetCompilationOptions()
{
return _options ?? base.GetCompilationOptions();
}
public DependencyContextOptions GetCompilationOptionsPublic() => base.GetCompilationOptions();
}
}
}

View File

@ -10,7 +10,10 @@
"../Microsoft.AspNetCore.Mvc.Razor.Host.Test/TestFileTrigger.cs",
"../Microsoft.AspNetCore.Mvc.ViewFeatures.Test/Internal/TestViewBufferScope.cs"
]
}
},
"define": [
"SOME_TEST_DEFINE"
]
},
"dependencies": {
"dotnet-test-xunit": "2.2.0-*",