diff --git a/src/Microsoft.AspNetCore.Mvc.Razor/Internal/DefaultRoslynCompilationService.cs b/src/Microsoft.AspNetCore.Mvc.Razor/Internal/DefaultRoslynCompilationService.cs index 8fbb8945f4..833c541949 100644 --- a/src/Microsoft.AspNetCore.Mvc.Razor/Internal/DefaultRoslynCompilationService.cs +++ b/src/Microsoft.AspNetCore.Mvc.Razor/Internal/DefaultRoslynCompilationService.cs @@ -27,6 +27,12 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal /// public class DefaultRoslynCompilationService : ICompilationService { + // error CS0234: The type or namespace name 'C' does not exist in the namespace 'N' (are you missing + // an assembly reference?) + private const string CS0234 = nameof(CS0234); + // error CS0246: The type or namespace name 'T' could not be found (are you missing a using directive + // or an assembly reference?) + private const string CS0246 = nameof(CS0246); private readonly DebugInformationFormat _pdbFormat = #if NET451 SymbolsUtility.SupportsFullPdbGeneration() ? @@ -128,18 +134,6 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal if (!result.Success) { - if (!compilation.References.Any() && !CompilationReferences.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, @@ -232,11 +226,23 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal sourceFileContent = ReadFileContentsSafely(_fileProvider, sourceFilePath); } + string additionalMessage = null; + if (group.Any(g => + string.Equals(CS0234, g.Id, StringComparison.OrdinalIgnoreCase) || + string.Equals(CS0246, g.Id, StringComparison.OrdinalIgnoreCase))) + { + additionalMessage = Resources.FormatCompilation_DependencyContextIsNotSpecified( + "preserveCompilationContext", + "buildOptions", + "project.json"); + } + var compilationFailure = new CompilationFailure( sourceFilePath, sourceFileContent, compilationContent, - group.Select(GetDiagnosticMessage)); + group.Select(GetDiagnosticMessage), + additionalMessage); failures.Add(compilationFailure); } diff --git a/src/Microsoft.AspNetCore.Mvc.Razor/Properties/Resources.Designer.cs b/src/Microsoft.AspNetCore.Mvc.Razor/Properties/Resources.Designer.cs index 9552b4a630..f53ef11e26 100644 --- a/src/Microsoft.AspNetCore.Mvc.Razor/Properties/Resources.Designer.cs +++ b/src/Microsoft.AspNetCore.Mvc.Razor/Properties/Resources.Designer.cs @@ -447,7 +447,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor } /// - /// The Razor page '{0}' failed to compile. Ensure that your application's {1} sets the '{2}' compilation property. + /// One or more compilation references are missing. Possible causes include a missing '{0}' property under '{1}' in the application's {2}. /// internal static string Compilation_DependencyContextIsNotSpecified { @@ -455,7 +455,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor } /// - /// The Razor page '{0}' failed to compile. Ensure that your application's {1} sets the '{2}' compilation property. + /// One or more compilation references are missing. Possible causes include a missing '{0}' property under '{1}' in the application's {2}. /// internal static string FormatCompilation_DependencyContextIsNotSpecified(object p0, object p1, object p2) { diff --git a/src/Microsoft.AspNetCore.Mvc.Razor/Resources.resx b/src/Microsoft.AspNetCore.Mvc.Razor/Resources.resx index 6e402095f5..c0d7484574 100644 --- a/src/Microsoft.AspNetCore.Mvc.Razor/Resources.resx +++ b/src/Microsoft.AspNetCore.Mvc.Razor/Resources.resx @@ -201,7 +201,7 @@ A circular layout reference was detected when rendering '{0}'. The layout page '{1}' has already been rendered. - The Razor page '{0}' failed to compile. Ensure that your application's {1} sets the '{2}' compilation property. + One or more compilation references are missing. Possible causes include a missing '{0}' property under '{1}' in the application's {2}. '{0}' cannot be empty. These locations are required to locate a view for rendering. diff --git a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/ErrorPageTests.cs b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/ErrorPageTests.cs index 818e2082bb..bc077e798e 100644 --- a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/ErrorPageTests.cs +++ b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/ErrorPageTests.cs @@ -16,6 +16,9 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests /// public class ErrorPageTests : IClassFixture> { + private static readonly string PreserveCompilationContextMessage = HtmlEncoder.Default.Encode( + "One or more compilation references are missing. Possible causes include a missing " + + "'preserveCompilationContext' property under 'buildOptions' in the application's project.json."); public ErrorPageTests(MvcTestFixture fixture) { Client = fixture.Client; @@ -23,21 +26,40 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests public HttpClient Client { get; } - [Theory] - [InlineData("CompilationFailure", "Cannot implicitly convert type 'int' to 'string'")] - [InlineData("ParserError", - "The code block is missing a closing "}" character. Make sure you " + - "have a matching "}" character for all the "{" characters " + - "within this block, and that none of the "}" characters are being " + - "interpreted as markup.")] - public async Task CompilationFailuresAreListedByErrorPageMiddleware(string action, string expected) + [Fact] + public async Task CompilationFailuresAreListedByErrorPageMiddleware() { // Arrange + var action = "CompilationFailure"; + var expected = "Cannot implicitly convert type 'int' to 'string'"; var expectedMediaType = MediaTypeHeaderValue.Parse("text/html; charset=utf-8"); // Act var response = await Client.GetAsync("http://localhost/" + action); + // Assert + Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode); + Assert.Equal(expectedMediaType, response.Content.Headers.ContentType); + var content = await response.Content.ReadAsStringAsync(); + Assert.Contains($"/Views/ErrorPageMiddleware/{action}.cshtml", content); + Assert.Contains(expected, content); + Assert.DoesNotContain(PreserveCompilationContextMessage, content); + } + + [Fact] + public async Task ParseFailuresAreListedByErrorPageMiddleware() + { + // Arrange + var action = "ParserError"; + var expected = "The code block is missing a closing "}" character. Make sure you " + + "have a matching "}" character for all the "{" characters " + + "within this block, and that none of the "}" characters are being " + + "interpreted as markup."; + var expectedMediaType = MediaTypeHeaderValue.Parse("text/html; charset=utf-8"); + + // Act + var response = await Client.GetAsync(action); + // Assert Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode); Assert.Equal(expectedMediaType, response.Content.Headers.ContentType); @@ -63,6 +85,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests var content = await response.Content.ReadAsStringAsync(); Assert.Contains("/Views/ErrorFromViewImports/_ViewImports.cshtml", content); Assert.Contains(expectedMessage, content); + Assert.Contains(PreserveCompilationContextMessage, content); } [Fact] diff --git a/test/Microsoft.AspNetCore.Mvc.Razor.Test/Internal/DefaultRoslynCompilationServiceTest.cs b/test/Microsoft.AspNetCore.Mvc.Razor.Test/Internal/DefaultRoslynCompilationServiceTest.cs index 957ddced77..6a320d51ad 100644 --- a/test/Microsoft.AspNetCore.Mvc.Razor.Test/Internal/DefaultRoslynCompilationServiceTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.Razor.Test/Internal/DefaultRoslynCompilationServiceTest.cs @@ -248,27 +248,6 @@ public class MyNonCustomDefinedClass {} Assert.Single(usedCompilation.Compilation.SyntaxTrees); } - [Fact] - public void Compile_ThrowsIfNoMetadataReferencesAreDiscoveredAndApplicationFailsToCompile() - { - // Arrange - var content = "public class MyTestType {}"; - var applicationPartManager = new ApplicationPartManager(); - var compilationService = GetRoslynCompilationService(applicationPartManager); - - 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(() => - compilationService.Compile(relativeFileInfo, content)); - Assert.Equal(expected, ex.Message); - } - [Fact] public void Compile_DoesNotThrowIfReferencesWereClearedInCallback() {