RazorCompilationService should throw a meaningful error when a user produces an application without `preserveCompilationContext`

Fixes #4202
This commit is contained in:
Pranav K 2016-03-09 14:26:29 -08:00
parent bf1fc7dab3
commit c03aabbff5
4 changed files with 143 additions and 1 deletions

View File

@ -85,7 +85,6 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
#if NETSTANDARD1_5
_razorLoadContext = new RazorLoadContext();
#endif
}
/// <inheritdoc />
@ -144,6 +143,18 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
if (!result.Success)
{
if (!compilation.References.Any() && !_applicationReferences.Value.Any())
{
// DependencyModel had no references specified and the user did not use the
// CompilationCallback to add extra references. It is likely that the user did not specify
// preserveCompilationContext in the app's project.json.
throw new InvalidOperationException(
Resources.FormatCompilation_DependencyContextIsNotSpecified(
fileInfo.RelativePath,
"project.json",
"preserveCompilationContext"));
}
return GetCompilationFailedResult(
fileInfo.RelativePath,
compilationContent,

View File

@ -462,6 +462,22 @@ namespace Microsoft.AspNetCore.Mvc.Razor
return string.Format(CultureInfo.CurrentCulture, GetString("LayoutHasCircularReference"), p0, p1);
}
/// <summary>
/// The Razor page '{0}' failed to compile. Ensure that your application's {1} sets the '{2}' compilation property.
/// </summary>
internal static string Compilation_DependencyContextIsNotSpecified
{
get { return GetString("Compilation_DependencyContextIsNotSpecified"); }
}
/// <summary>
/// The Razor page '{0}' failed to compile. Ensure that your application's {1} sets the '{2}' compilation property.
/// </summary>
internal static string FormatCompilation_DependencyContextIsNotSpecified(object p0, object p1, object p2)
{
return string.Format(CultureInfo.CurrentCulture, GetString("Compilation_DependencyContextIsNotSpecified"), p0, p1, p2);
}
private static string GetString(string name, params string[] formatterNames)
{
var value = _resourceManager.GetString(name);

View File

@ -203,4 +203,7 @@
<data name="LayoutHasCircularReference" xml:space="preserve">
<value>A circular layout reference was detected when rendering '{0}'. The layout page '{1}' has already been rendered.</value>
</data>
<data name="Compilation_DependencyContextIsNotSpecified" xml:space="preserve">
<value>The Razor page '{0}' failed to compile. Ensure that your application's {1} sets the '{2}' compilation property.</value>
</data>
</root>

View File

@ -256,6 +256,7 @@ public class MyNonCustomDefinedClass {}
[Fact]
public void Compile_RunsCallback()
{
// Arrange
var content = "public class MyTestType {}";
RoslynCompilationContext usedCompilation = null;
@ -276,6 +277,117 @@ public class MyNonCustomDefinedClass {}
Assert.Single(usedCompilation.Compilation.SyntaxTrees);
}
[Fact]
public void Compile_ThrowsIfDependencyContextIsNullAndTheApplicationFailsToCompileWithNoReferences()
{
// Arrange
var content = "public class MyTestType {}";
var compilationService = new DefaultRoslynCompilationService(
dependencyContext: null,
viewEngineOptions: GetOptions(),
fileProviderAccessor: GetFileProviderAccessor(),
loggerFactory: NullLoggerFactory.Instance);
var relativeFileInfo = new RelativeFileInfo(
new TestFileInfo { PhysicalPath = "SomePath" },
"some-relative-path.cshtml");
var expected = "The Razor page 'some-relative-path.cshtml' failed to compile. Ensure that your "
+ "application's project.json sets the 'preserveCompilationContext' compilation property.";
// Act and Assert
var ex = Assert.Throws<InvalidOperationException>(() =>
compilationService.Compile(relativeFileInfo, content));
Assert.Equal(expected, ex.Message);
}
[Fact]
public void Compile_ThrowsIfDependencyContextReturnsNoReferencesAndTheApplicationFailsToCompile()
{
// Arrange
var content = "public class MyTestType {}";
var dependencyContext = new DependencyContext(
target: null,
runtime: null,
compilationOptions: Extensions.DependencyModel.CompilationOptions.Default,
compileLibraries: new CompilationLibrary[0],
runtimeLibraries: new RuntimeLibrary[0]);
var compilationService = new DefaultRoslynCompilationService(
dependencyContext: dependencyContext,
viewEngineOptions: GetOptions(),
fileProviderAccessor: GetFileProviderAccessor(),
loggerFactory: NullLoggerFactory.Instance);
var relativeFileInfo = new RelativeFileInfo(
new TestFileInfo { PhysicalPath = "SomePath" },
"some-relative-path.cshtml");
var expected = "The Razor page 'some-relative-path.cshtml' failed to compile. Ensure that your "
+ "application's project.json sets the 'preserveCompilationContext' compilation property.";
// Act and Assert
var ex = Assert.Throws<InvalidOperationException>(() =>
compilationService.Compile(relativeFileInfo, content));
Assert.Equal(expected, ex.Message);
}
[Fact]
public void Compile_DoesNotThrowIfReferencesWereClearedInCallback()
{
// Arrange
var options = GetOptions(context =>
{
context.Compilation = context.Compilation.RemoveAllReferences();
});
var content = "public class MyTestType {}";
var compilationService = new DefaultRoslynCompilationService(
dependencyContext: GetDependencyContext(),
viewEngineOptions: options,
fileProviderAccessor: GetFileProviderAccessor(),
loggerFactory: NullLoggerFactory.Instance);
var relativeFileInfo = new RelativeFileInfo(
new TestFileInfo { PhysicalPath = "SomePath" },
"some-relative-path.cshtml");
// Act
var result = compilationService.Compile(relativeFileInfo, content);
// Assert
Assert.Single(result.CompilationFailures);
}
[Fact]
public void Compile_SucceedsIfReferencesAreAddedInCallback()
{
// Arrange
var options = GetOptions(context =>
{
var assemblyLocation = typeof(object).GetTypeInfo().Assembly.Location;
context.Compilation = context
.Compilation
.AddReferences(MetadataReference.CreateFromFile(assemblyLocation));
});
var content = "public class MyTestType {}";
var compilationService = new DefaultRoslynCompilationService(
dependencyContext: null,
viewEngineOptions: options,
fileProviderAccessor: GetFileProviderAccessor(),
loggerFactory: NullLoggerFactory.Instance);
var relativeFileInfo = new RelativeFileInfo(
new TestFileInfo { PhysicalPath = "SomePath" },
"some-relative-path.cshtml");
// Act
var result = compilationService.Compile(relativeFileInfo, content);
// Assert
Assert.Null(result.CompilationFailures);
Assert.NotNull(result.CompiledType);
}
private static DiagnosticDescriptor GetDiagnosticDescriptor(string messageFormat)
{
return new DiagnosticDescriptor(