Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does Razor ViewEngine cache the rendered HTMLs?

I have a 2-level menu item: I have a list of department and each department has a list of stores.

I have a Menu, PartialView which iterates through the Model (departments) and builds the menu:

@model IEnumerable<Department>

<ul>
    @foreach (var department in Model)
    {
        <li>
            <a href="#">@Model.DepartmentName</a>
            <ul>
                @foreach (var store in department.Stores)
                {
                    <li><a href="some-url">@store.StoreName</a></li>
                }
            </ul>
        </li>
    }
</ul>

And this is how I call the Menu PartialView in my _layout.cshtml:

@Html.Partial("Shared/_Menu", MyApplicationCache.departments) 

As you can see I am passing the same model (from the cache) to the PartialView on all the requests.

Does Razor ViewEngine have an internal caching system to recognize that this view has already been built (complied to HTML string) for this model? Or does it re-render (recompile) the PartialView on every single request?

like image 399
Hooman Bahreini Avatar asked Nov 27 '18 07:11

Hooman Bahreini


Video Answer


2 Answers

The PartialView gets re-rendered at every single request, assuming you don't have any OutputCacheAttribute applied on the Controller or its action method involved.

If you need output caching, you need to set this up explicitly via OutputCacheAttribute, see the documentation.

You can easily check this by outputting a DateTime, eg. via a menu-item as shown here below.
At every request, it will show a new value, proving it got re-rendered.

<li><a href="#">@DateTime.Now</a></li>

Full menu:

@model IEnumerable<Department>

<ul>
    @foreach (var department in Model)
    {
        <li>
            <a href="#">@Model.DepartmentName</a>
            <ul>
                <li><a href="#">@DateTime.Now</a></li>
                @foreach (var store in department.Stores)
                {
                    <li><a href="some-url">@store.StoreName</a></li>
                }                
            </ul>
        </li>
    }
</ul>
like image 119
pfx Avatar answered Oct 15 '22 23:10

pfx


Re-Rendering and Re-Compiling are very very different in ASP.Net MVC. While most of the answers here are correct, the View is only compiled once (except in debug mode, where it's compiled each time so you can change the view, hit refresh and see the change, or if the timestamp changes on the file in production). It is compiled into a runtime class that derives from WebViewpage(no ViewModel) or WebViewPage<T>(has ViewModel of type T).

The class is instantiated for each view needed (so if you used the same partial multiple times, you have to instantiate the each time), the model is populated, and the execute() method is called to create/stream the HTML to the client. The view could never be cached per model as it is to complicated to do that and instead the MVC team chose to allow configuring caching per controller method, not per WebViewPage.

@ErikPhilips, thanks a lot for this - So the View is only compiled once (no matter if we use or not use OutputCache)? It's the execute method which renders the runtime class into HtmlString, and it is the rendering which would benefit from caching?

Sorta of, but it's much more advanced, easier and complicated then that.

Advanced - The output cache is based on the controller method. So if the output cache configuration determines that the call can use a cached version, the controller method is never called. That's where the huge performance gain is. Imagine no call to the DB / external API call, there isn't a need. You could configure the cache so if it sees id=1 cache it for 30 minutes. Now anyone who calls that method with authorization and id=1 gets the cached string/html.

Easier - You put your OuputCacheAttribute on a method, configure it and you're done. Pretty darn easy to configure.

Complicated - Caching can be more complicated because you can render other controller methods using Html.Action() (Html.Partial() if you don't need a layout for your partial) or the preferred Html.RenderAction(); (Html.RenderPartial() if you don't need a layout). There use to be a Donut Hole Caching Issue (recommended reading) but that's been fixed for a long time.

like image 44
Erik Philips Avatar answered Oct 16 '22 00:10

Erik Philips