1
votes

I recently created a small ASP.Net Core MVC project, and placed all my .cshtml files in the Views directory. Then I using the following code in Startup.cs to change where Razor searches for the view files:

services.Configure<RazorViewEngineOptions>(options => {
    //{2} is area, {1} is controller, {0} is the action    
    options.ViewLocationFormats.Clear();
    options.ViewLocationFormats.Add("/Views/{1}" + RazorViewEngine.ViewExtension);
});

The .cshtml files map to a corresponding controller name, where all controllers contain a default "Index" action that simply returns the associated View().

This is the current routing:

app.UseMvc(routes => {
    routes.MapRoute(
        name: "default",
        template: "{controller=Index}/{action=Index}/{id?}");
});

In my eyes this configuration should work, however a "NotSupportedException" is being thrown by Razor whenever I try to invoke any page, and I am unsure how to debug this:

NotSupportedException: Specified method is not supported.
Microsoft.AspNetCore.Mvc.RazorPages.PageBase.EnsureRenderedBodyOrSections()
Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderLayoutAsync(ViewContext context, ViewBufferTextWriter bodyWriter)
Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderAsync(ViewContext context)
Microsoft.AspNetCore.Mvc.ViewFeatures.ViewExecutor.ExecuteAsync(ViewContext viewContext, string contentType, Nullable<int> statusCode)
Microsoft.AspNetCore.Mvc.ViewFeatures.ViewExecutor.ExecuteAsync(ActionContext actionContext, IView view, ViewDataDictionary viewData, ITempDataDictionary tempData, string contentType, Nullable<int> statusCode)
Microsoft.AspNetCore.Mvc.ViewFeatures.ViewResultExecutor.ExecuteAsync(ActionContext context, ViewResult result)
Microsoft.AspNetCore.Mvc.ViewResult.ExecuteResultAsync(ActionContext context)
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeResultAsync(IActionResult result)
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeNextResultFilterAsync<TFilter, TFilterAsync>()
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Rethrow(ResultExecutedContext context)
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.ResultNext<TFilter, TFilterAsync>(ref State next, ref Scope scope, ref object state, ref bool isCompleted)
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeResultFilters()
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeNextResourceFilter()
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Rethrow(ResourceExecutedContext context)
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Next(ref State next, ref Scope scope, ref object state, ref bool isCompleted)
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeFilterPipelineAsync()
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeAsync()
Microsoft.AspNetCore.Builder.RouterMiddleware.Invoke(HttpContext httpContext)
Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware.Invoke(HttpContext context)
Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

I looked at the source code of EnsureRenderedBodyOrSections(), which is simply the following:

public override void EnsureRenderedBodyOrSections() {
    //This will never be called by MVC. MVC only calls this method on layout pages, and a Page can never be a layout page.
    throw new NotSupportedException();
}

MVC is apparently calling the function when it's not supposed to. What exactly is the issue that's at play here?

PS: This is the project on GitHub: https://github.com/mathusummut/nemesys-stackoverflow

1
So you have a controller named Index? template: "{controller=Index} in your routing?Ryan Wilson
Yes, and it is meant to show the homepage which is Index.cshtml.MathuSum Mut
Yeah. A github link would probably be helpful.Chris Pratt
It appears you're trying to use Razor Pages as views for an MVC application. If you're returning a View(...) result from a Controller, you should not be using a Page for the view. A Page is a .cshtml file with a @page directive at the top. These technologies are meant to be mutually exclusive for a given request path. You can use them both in the same application, but not in the same request. I would recommend removing the @page directive from your views and going from there.Arcanox
I'll write it up as one now :)Arcanox

1 Answers

2
votes

The reason the EnsureRenderedBodyOrSections method is being called is because you're returning a View() result from a Controller, and this method is part of the rendering lifecycle for Views. Your Razor files, however, have @page directives at the top, which makes them into Page files for Razor Pages.

While you can use MVC Views and Razor Pages in the same application, they cannot be mixed for one request path. If you remove the @page directive from your View files, they will be compiled as Razor Views and not Razor Pages, so you will not get a NotSupportedException.

As a side note, I would recommend removing the line options.ViewLocationFormats.Clear(); and keep the default view location, so that you can use the Views/{Controller}/{Action} view location in the future while still being able to have a view file named Views/{Controller}. That way as you add more functionality to the now-MVC application, you can do so using traditional MVC patterns.