Razor ViewEngine是否缓存呈现的HTML?

时间:2018-11-27 07:03:57

标签: asp.net-mvc razor partial-views viewengine

我有一个2级菜单项:我有一个部门列表,每个部门都有一个商店列表。

我有一个PartialView菜单,它遍历Model(部门)并构建菜单:

@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>

这就是我在_layout.cshtml中调用菜单PartialView的方式:

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

如您所见,我在所有请求上都将相同的模型(从缓存中)传递给PartialView。

Razor ViewEngine是否具有内部缓存系统来识别该视图已经为此模型建立(转换为HTML字符串)?还是在每个单个请求上重新渲染(重新编译)PartialView?

3 个答案:

答案 0 :(得分:4)

假设您没有在PartialView上应用任何OutputCacheAttribute或其所涉及的操作方法,则在每次请求时都会重新呈现Controller

如果需要输出缓存,则需要通过OutputCacheAttribute进行显式设置,请参见documentation

您可以通过输出DateTime轻松地进行检查,例如。通过菜单项,如下所示。
在每次请求时,它将显示一个新值,证明它已被重新渲染。

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

完整菜单:

@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>

答案 1 :(得分:2)

重新渲染和重新编译在ASP.Net MVC中非常不同。尽管此处的大多数答案都是正确的,但视图仅编译一次(在调试模式下除外,调试模式下每次都会编译一次,因此您可以更改视图,点击刷新并查看更改,或者生产中的文件上的时间戳更改)。它被编译成一个运行时类,该运行时类从WebViewpage(无ViewModel)或WebViewPage<T>(具有T型ViewModel)派生。

为所需的每个视图实例化该类(因此,如果多次使用相同的局部视图,则必须每次实例化),填充模型,并调用execute()方法创建/流式传输HTML给客户。该视图永远无法按模型缓存,因为这样做很复杂,因此MVC团队选择允许按控制器方法(而不是按WebViewPage)配置缓存。

  

@ErikPhilips,对此非常感谢-因此View仅被编译一次(无论我们是否使用OutputCache)?是将运行时类呈现为HtmlString的execute方法,而正是从缓存中受益的呈现?

的主旋律,但它要先进得多,更容易且更复杂。

高级-输出缓存基于控制器方法。因此,如果输出缓存配置确定该调用可以使用缓存版本,则控制器方法不会被调用。那就是巨大的性能提升所在。想象没有对数据库的调用/对外部API的调用,没有必要。您可以配置缓存,以便如果它看到id=1缓存30分钟。现在,任何使用授权和id=1来调用该方法的人都可以获取缓存的字符串/ html。

更轻松-将OuputCacheAttribute放在方法上,对其进行配置即可。很容易配置。

复杂-可以使用more complicated进行缓存,因为您可以使用Html.Action()(如果不需要局部布局,可以使用Html.Partial()或首选的{{1 }}(Html.RenderAction();,如果您不需要版面)。曾经有Donut Hole Caching Issue(推荐读物),但是已经修复了很长时间了。

答案 2 :(得分:1)

这个问题有一个很好的答案,证明不缓存PartialViews,注释中建议的this link解释了如何对部分View使用[OutputCache]-可以与{{1 }} / Html.Action()并将PartialViews呈现为Html.RenderAction()

将PartialView缓存为子动作是有道理的,但是我不想将菜单呈现为[ChildAction],因为我不想单独调用显示菜单,所以这就是我的结果在做:

我使用RazorEngine在应用程序启动时将PartialView呈现到HtmlString中,并将HtmlString保留在静态变量(缓存)中。

[ChildAction]

我还创建了一个自定义的HtmlHelper方法,该方法将返回菜单的HtmlString:

public static class MenuCache
{
    private static readonly MvcHtmlString _menuMvcHtmlString;

    static MenuCache()
    {
        using (var context = ApplicationDbContext.Create())
        using (var razorEngine = RazorEngineService.Create(new TemplateServiceConfiguration()))
        {
            var repository = new MyRepository(context);
            var departments = repository.GetDepartments();

            // use razorEngine to render menu partial view into html string 
            // keep the htmlString in cache: _menuMvcHtmlString
            string menuPartialView = File.ReadAllText(HostingEnvironment.MapPath("~/Views/Shared/_Menu.cshtml"));
            string menuHtmlString = razorEngine.RunCompile(menuPartialView, "menuKey", null, departments);
            _menuMvcHtmlString = new MvcHtmlString(menuHtmlString);
        }
    }

    public static MvcHtmlString GetMenuHtmlString()
    {
        return _menuMvcHtmlString;
    }
}

现在,在我的_Layout页面中,我可以使用自定义的public static class HtmlHelperExtensions { public static MvcHtmlString MyMenu(this HtmlHelper html) { return MenuCache.GetMenuHtmlString(); } } 来显示菜单:

HtmlHelper