Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Razer escape @@ results in build error since SDK 8.0.400 #42730

Closed
ronymeyer opened this issue Aug 14, 2024 · 13 comments
Closed

Razer escape @@ results in build error since SDK 8.0.400 #42730

ronymeyer opened this issue Aug 14, 2024 · 13 comments
Labels
Area-AspNetCore RazorSDK, BlazorWebAssemblySDK, dotnet-watch untriaged Request triage from a team member

Comments

@ronymeyer
Copy link

Describe the bug

Since preview of 8.0.400 and now also in final version of 8.0.400 compilation of razor pages containing @@ fail with:

FileName.cshtml: error CS9008: Sequence of '@' characters is not allowed. A verbatim string or identifier can only have one '@' character and a raw string cannot have any.

To Reproduce

Any Razor page with code like this fails:
@{ var validationMessage = @Html.ValidationMessage(Model.Binding, "", new { @@class = "invalid-feedback" }, "div"); }

Changing from @@Class to @Class makes the dotNet compilation work, but then the Razor compilation fails with:
Error RZ1007: "class" is a reserved word and cannot be used in implicit expressions. An explicit expression ("@()") must be used.

@dotnet-issue-labeler dotnet-issue-labeler bot added Area-AspNetCore RazorSDK, BlazorWebAssemblySDK, dotnet-watch untriaged Request triage from a team member labels Aug 14, 2024
@baronfel
Copy link
Member

cc @jaredpar / @chsienki

@chsienki
Copy link
Contributor

@333fred

@333fred
Copy link
Member

333fred commented Aug 14, 2024

This is an intentional breaking change in dotnet/razor#10232. We should certainly document it better, but when you're inside a C# context already, @@ should never have been permitted here. Changing to @class is the correct syntax.

@333fred
Copy link
Member

333fred commented Aug 14, 2024

Also, for the error that you get when you change that to @class: are you seeing that in the IDE only? Or are you seeing it from dotnet build? We're not seeing it in dotnet build, so if you are seeing it there, it would be great if you could share a complete repro.

@ronymeyer
Copy link
Author

No, the error with @Class doesn't happen in the IDE, it only happens in runtime.

Stack trace:
at Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation.RuntimeViewCompiler.CompileAndEmit(String relativePath) at Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation.RuntimeViewCompiler.OnCacheMiss(String normalizedPath) --- End of stack trace from previous location --- at Microsoft.AspNetCore.Mvc.Razor.Compilation.DefaultRazorPageFactoryProvider.CreateFactory(String relativePath) at Microsoft.AspNetCore.Mvc.Razor.RazorViewEngine.CreateCacheResult(HashSet1 expirationTokens, String relativePath, Boolean isMainPage)
at Microsoft.AspNetCore.Mvc.Razor.RazorViewEngine.OnCacheMiss(ViewLocationExpanderContext expanderContext, ViewLocationCacheKey cacheKey)
at Microsoft.AspNetCore.Mvc.Razor.RazorViewEngine.LocatePageFromViewLocations(ActionContext actionContext, String pageName, Boolean isMainPage)
at Microsoft.AspNetCore.Mvc.Razor.RazorViewEngine.FindView(ActionContext context, String viewName, Boolean isMainPage)
at Microsoft.AspNetCore.Mvc.ViewEngines.CompositeViewEngine.FindView(ActionContext context, String viewName, Boolean isMainPage)
at Microsoft.AspNetCore.Mvc.ViewComponents.ViewViewComponentResult.ExecuteAsync(ViewComponentContext context)
at Microsoft.AspNetCore.Mvc.ViewComponents.DefaultViewComponentInvoker.InvokeAsync(ViewComponentContext context)
at Microsoft.AspNetCore.Mvc.ViewComponents.DefaultViewComponentHelper.InvokeCoreAsync(ViewComponentDescriptor descriptor, Object arguments)
at AspNetCore.Areas_AccountPkg_Views_AccountBalance_AccountBalancePanel.ExecuteAsync() in C:\Users\RonyMeyer\source\repos\FinanceKey\MVC\Areas\AccountPkg\Views\AccountBalance\AccountBalancePanel.cshtml:line 63
at Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderPageCoreAsync(IRazorPage page, ViewContext context)
at Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderPageAsync(IRazorPage page, ViewContext context, Boolean invokeViewStarts)
at Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderAsync(ViewContext context)
at Microsoft.AspNetCore.Mvc.ViewFeatures.ViewExecutor.ExecuteAsync(ViewContext viewContext, String contentType, Nullable1 statusCode) at Microsoft.AspNetCore.Mvc.ViewFeatures.ViewExecutor.ExecuteAsync(ViewContext viewContext, String contentType, Nullable1 statusCode)
at Microsoft.AspNetCore.Mvc.ViewFeatures.ViewExecutor.ExecuteAsync(ActionContext actionContext, IView view, ViewDataDictionary viewData, ITempDataDictionary tempData, String contentType, Nullable1 statusCode) at Microsoft.AspNetCore.Mvc.ViewFeatures.ViewResultExecutor.ExecuteAsync(ActionContext context, ViewResult result) at Microsoft.AspNetCore.Mvc.ViewResult.ExecuteResultAsync(ActionContext context) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Logged|22_0(ResourceInvoker invoker, IActionResult result) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|30_0[TFilter,TFilterAsync](ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResultExecutedContextSealed context) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.ResultNext[TFilter,TFilterAsync](State& next, Scope& scope, Object& state, Boolean& isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|28_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|25_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|20_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Logged|17_1(ResourceInvoker invoker) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Logged|17_1(ResourceInvoker invoker) at Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext) at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider) at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context) at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context) at Microsoft.AspNetCore.Localization.RequestLocalizationMiddleware.Invoke(HttpContext context) at MVC.Startup.<>c__DisplayClass9_0.<b__0>d.MoveNext() in C:\Users\RonyMeyer\source\repos\FinanceKey\MVC\Startup.cs:line 339 --- End of stack trace from previous location --- at Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddlewareImpl.g__Awaited|10_0(ExceptionHandlerMiddlewareImpl middleware, HttpContext context, Task task)

Exception details:
Type = Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation.CompilationFailedException Source = Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation CompilationFailures = [0] = Microsoft.AspNetCore.Diagnostics.CompilationFailure TargetSite = Microsoft.AspNetCore.Mvc.Razor.Compilation.CompiledViewDescriptor CompileAndEmit(System.String) HResult = -2146233088

@jjonescz
Copy link
Member

it only happens in runtime.

It looks like you are using runtime compilation (e.g., via AddRazorRuntimeCompilation in your Startup.cs). That's a legacy system which doesn't support new features. In particular, it uses an old compiler version which explains why it fails with that error. Do you need to be using runtime compilation for some reason?

@ronymeyer
Copy link
Author

Yes, we are using runtime compilation. The reason is that we have the design of our Razor pages defined in data. This allows us to modify pages (e.g. add new controls, change position, ...) and deploy them in the running app. Deploying in this case is just writing the new cshtml file out. This makes the design process super fast and easy without having to make a full release.
Do you see any way how we could still do this?

@jjonescz
Copy link
Member

Hot reloading should provide similar functionality (i.e., changing a cshtml file should result in the code changes being reflected in the running app without full rebuild).

@ronymeyer
Copy link
Author

I can confirm that when I remove AddRazorRuntimeCompilation everything works.

Looking at hot reloading, this seems to only work in the IDE, correct? We have the app currently deployed to a "development" environment with AddRazorRuntimeCompilation enabled where non developers can modify and deploy the razor pages and see the changes immediately reflected.

@jjonescz
Copy link
Member

Looking at hot reloading, this seems to only work in the IDE, correct?

In IDE but also if you are running dotnet watch on the command line. But of course, it's an interactive command built for development, might not work as intended on a development environment server that you describe.

We have the app currently deployed to a "development" environment with AddRazorRuntimeCompilation enabled where non developers can modify and deploy the razor pages and see the changes immediately reflected.

cc @danroth27 for razor runtime compilation use case

@ronymeyer
Copy link
Author

We deploy to an Azure WebApp, so that is not possible.

So for the moment it looks like we have to disable razor runtime compilation so that we can work with the latest SDK.
Is there any further development of the razor runtime compilation planned to bring it on par with the compiled version or is this feature EOL?

333fred added a commit to 333fred/razor that referenced this issue Aug 15, 2024
… the new lexer, these will once again be disallowed; attempting to use runtime compilation will necessitate using the native lexer.
@333fred
Copy link
Member

333fred commented Aug 15, 2024

In order to lessen the severity of this breaking change, we've decided to allow @@ in these types of locations for now. We will break this again in the future when we move to the new Roslyn-based lexer; while that will be an opt-in feature in the first releases, it will become on by default sometime during the .NET 9 lifecycle. When it does, working around this and using runtime compilation will only be possible by explicitly turning off the new Roslyn-based lexer and using the older (current) native lexer.

333fred added a commit to 333fred/razor that referenced this issue Aug 15, 2024
… the new lexer, these will once again be disallowed; attempting to use runtime compilation will necessitate using the native lexer.
@ronymeyer
Copy link
Author

I was looking into how to handle this for the moment so that we can use 8.0.400 SDK for our normal builds and the builds where we use runtime compilation use 8.0.304. It turned out that preprocessor directives didn't work as expected in .cshtml.
I then created an html helper like:
public static IHtmlContent GetValidationMessage(this IHtmlHelper helper, string binding) { return helper.ValidationMessage(binding, "", new { @class = "invalid-feedback" }, "div"); }
I think this is the best solution to go with as it doesn't require the @@ and we can use the same SDK for all builds.
I know you already rolled back your code change, but with this it would work and I think it could also be applied to any other scenario where @@ was required so far.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-AspNetCore RazorSDK, BlazorWebAssemblySDK, dotnet-watch untriaged Request triage from a team member
Projects
None yet
Development

No branches or pull requests

5 participants