【问题标题】:Does Razor ViewEngine cache the rendered HTMLs?Razor View Engine 是否缓存呈现的 HTML?
【发布时间】:2019-04-28 21:06:28
【问题描述】:

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

我有一个 Menu,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 中调用 Menu PartialView 的方式:

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

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

Razor ViewEngine 是否有一个内部缓存系统来识别这个视图已经为这个模型构建(符合 HTML 字符串)?或者它是否在每个请求上重新渲染(重新编译)PartialView?

【问题讨论】:

  • @DaleBurrell:非常感谢。所以阅读你的链接,我可以看到 RazorView 没有缓存任何东西......除非使用 [OutputCache]。
  • 重新渲染和重新编译在 ASP.Net MVC 中非常不同。虽然这里的大多数答案都是正确的,但视图只编译一次。它被编译成一个运行时类。为所需的每个视图实例化该类,填充模型,并调用 execute() 方法来创建/流式传输 HTML 到客户端。视图永远不能为每个模型缓存,因为这样做很复杂,相反,MVC 团队选择允许为每个控制器方法配置缓存。
  • @ErikPhilips,非常感谢 - 所以视图只编译一次(无论我们是否使用 OutputCache)?是execute方法将运行时类渲染成HtmlString,是渲染受益于缓存?

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


【解决方案1】:

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

如果您需要输出缓存,则需要通过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>

【讨论】:

    【解决方案2】:

    重新渲染和重新编译在 ASP.Net MVC 中非常不同。虽然这里的大多数答案都是正确的,但 View 只编译一次(调试模式除外,它每次都会编译,因此您可以更改视图、点击刷新并查看更改,或者如果生产中文件的时间戳更改)。它被编译为派生自WebViewpage(无 ViewModel)或WebViewPage&lt;T&gt;(具有 T 类型的 ViewModel)的运行时类。

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

    @ErikPhilips,非常感谢 - 所以视图只编译一次(无论我们是否使用 OutputCache)?是execute方法将运行时类渲染成HtmlString,是渲染受益于缓存?

    有点,但它比那更先进、更容易和更复杂。

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

    更简单 - 你把你的OuputCacheAttribute 放在一个方法上,配置它,你就完成了。非常容易配置。

    复杂 - 缓存可以是 more complicated,因为您可以使用 Html.Action()Html.Partial(),如果您不需要部分布局)或首选 Html.RenderAction();Html.RenderPartial(),如果您不需要布局)。曾经有一个Donut Hole Caching Issue(推荐阅读),但已经修复了很长时间。

    【讨论】:

      【解决方案3】:

      这个问题有一个很好的答案,它证明 PartialViews 没有被缓存,并且 cmets 中建议的 this link 解释了如何将 [OutputCache] 用于 partialView - 这可以与 Html.Action() / Html.RenderAction() 一起使用并将 PartialViews 呈现为 [ChildAction]

      将 PartialView 缓存为子操作是有意义的,但我不想将我的菜单呈现为 [ChildAction],因为我不想单独调用来显示菜单,所以这就是我最终要做的:

      我使用RazorEngine 在应用程序启动时将我的 PartialView 呈现为 HtmlString,并将 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;
          }
      }
      

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

      public static class HtmlHelperExtensions
      {
          public static MvcHtmlString MyMenu(this HtmlHelper html)
          {
              return MenuCache.GetMenuHtmlString();
          }
      }
      

      现在在我的_Layout页面中,我可以使用自定义的HtmlHelper来显示菜单:

      @Html.MyMenu()
      

      【讨论】:

      • 与使用具有ChildOnlyActionAttributeOutputCacheAttribute 的方法创建控制器相比,这似乎非常复杂。
      • @ErikPhilips - 再次感谢。但是如果我在 Partial 视图上创建一个 OutputCache,那么每次渲染视图时,我都需要再次调用来获取菜单,对吗?考虑到菜单出现在我的所有视图之上,我认为单独缓存它可能是个好主意,以避免第二次调用:Html.RenderAction()...我在这里遗漏了什么吗?
      • 第二个调用被缓存后谁在乎?如果缓存了,则返回的只是 HTML。
      猜你喜欢
      • 1970-01-01
      • 2011-04-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-11-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多