【问题标题】:What is the execution order of an MVC Razor view/layoutMVC Razor 视图/布局的执行顺序是什么
【发布时间】:2013-08-15 03:45:15
【问题描述】:

我有一个剃须刀布局,例如:

@using (var context = SetUpSomeContext()) {
    <div>
        Some content here
        @RenderBody();
    </div>
}

还有这样的视图:

@{
    Layout = "MyLayout.cshtml";
}
<div>@SomethingThatDependsOnContextBeingSetUp()</div>

当视图呈现时,SomethingThatDependsOnContextBeingSetUpSetUpSomeContext 之前执行并失败。这看起来很奇怪,因为我希望在布局中调用 RenderBody 之前不会执行。当我将其切换为使用“PageContent”部分而不是 RenderBody 时,一切都按预期工作。谁能解释这种行为?

【问题讨论】:

  • 我的回答解释了这种行为,还有什么要解决的?

标签: c# asp.net-mvc-3 razor


【解决方案1】:

Razor 管道是:

  1. 首先,Razor 评估仅包含用于分配布局或其他初始化的 Razor 语句(C# 或 VB)的 _ViewStart.cshtml(如果存在),它不应包含 html 标记。

  2. 然后,它解析并评估“查看”cshtml 文件

  3. 然后,它解析并评估(如果存在)布局,并在评估 cshtml 布局文件的 @RenderBody 方法时,将其替换为评估结果的 html 脚本“查看”cshtml 文件。

  4. 最后,构建布局和视图html文件的html控制图对象。


所以,您不能从布局操作中依赖视图的任何“Razor”对象,而是您可以在_ViewStart.cshtml 中放入您的初始化对您的视图可见的对象。


您可以将 cs(vb)html 视图想象为调用 Controller.View 方法时加载的静态内容。

此时,加载的 cshtml 内容由 Razor 解析,Razor 评估表达式(分配属性(作为布局)、分支、循环)并构建一种二叉树或“HtmlControls”对象图到ActionResult View 方法返回的对象。

接下来,ActionResult 从 Asp.Net 呈现为 html 并作为 http 响应返回给客户端。

为此,Razor 解析 cshtml 文件并在部分内部执行它们的代码,首先从“_ViewStart.cshtml”开始(如果存在于与源控制器相关的子文件夹链中,还有多个),然后跟随 cshtml 文件通过约定加载(视图名称等于路径 Views/[ControllerName]/ 中的操作名称),或者在调用View 方法时通过表达视图的名称作为参数,最后通过链接到视图的最终布局文件Layout 财产。

【讨论】:

    【解决方案2】:

    让我通过调查一个情况来澄清这一点,假设你有这样的看法;

     @renderSection("Header")
        @using (var context = SetUpSomeContext()) {
            <div>
                Some content here
                @RenderBody();
            </div>
        }
       @renderSection("Footer")
    

    并且我们假设 razor 按照您期望的顺序执行页面,如果我们声明我们的视图会发生什么?

    @{
        Layout = null;
    }
    <div>@SomethingThatDependsOnContextBeingSetUp()</div>
    

    在执行 @RenderBody() 之前,Razor 不会知道该视图是否需要布局页面。此外,它会推断它无缘无故渲染布局页面,这是不合理的。所以这个实际情况并非如此。

    当请求发出时,Razor 首先执行视图的主体是很自然的。 如果您的视图没有像我的演示那样指定布局 Razor 只会呈现该页面的输出并停在那里。如果视图具有在您的代码中指定的布局 执行视图后,它将控制权传递给布局页面。(布局页面开始从上到下渲染)所以布局页面剩下的只是内容放置。当它看到@RenderBody()时,它只会放置您已经执行的输出查看。

    对于部分;当您的视图主体执行时,它们不会被执行,在您的视图将控制权传递给布局页面后,布局页面会按照声明的顺序显式调用您的部分的执行。

    还要注意,您在视图正文中指定了页面标题,并且它在布局标题标签中呈现 (ViewBag.Title)。执行视图正文后,视图中声明的所有变量正文在布局页面中可用。

    求和:渲染顺序是从上到下,但执行顺序不同。

    根据您的情况“SomethingThatDependsOnContextBeingSetUp 在 SetUpSomeContext 之前执行并失败”。就像我说的那样,这是 Razor 执行周期的自然行为,视图主体在布局页面执行之前执行。当你制作它时;首先执行视图主体,但在布局页面之前不执行部分。视图主体将控制权传递给布局页面,布局页面开始从上到下渲染,如果看到@RenderSection,则调用部分的执行。因此在这种情况下执行 SetUpSomeContext在SomethingThatDependsOnContextBeingSetUp执行之前。

    【讨论】:

    • 这不是答案,应该移到问题的一部分。
    • 我认为它解释了提问者需要学习的每一个细节
    • 这看起来很有用,但不像一个答案,更像是一个通用教程。
    • 我刚刚编辑了我的答案以更清楚我认为现在很明显
    • 更简洁地说,Razor 直到处理完主视图后才能知道您有布局,然后它会查看布局是否存在,如果存在则处理它。
    【解决方案3】:

    如果您对所有视图都需要一些逻辑,请创建一个 ViewModelBase,您的所有 ViewModels 都从该 ViewModels 继承。

    然后在您的Controller(Base) 中您可以初始化ViewModel.SharedContext 和其他属性。

    【讨论】:

    • 我不希望在所有视图中都使用这种逻辑,而只是在整页中。您仍然会建议在布局上使用 ViewModelBase 吗?另外,我怎样才能使用这种技术在视图的开头和结尾运行代码(例如,创建一个上下文然后释放它)?
    • @Chase 你在说什么上下文?鉴于您遇到的问题,我建议您在不同的层面解决这个问题,就像我说的那样,例如使用共享的 ViewModel。
    【解决方案4】:

    执行顺序是从最里面到最外面。

    我认为使用“上下文”的方式不是最好的设计 - 您应该考虑将设置移动到控制器/动作过滤器并将数据传递到模型中的视图。

    【讨论】:

    • 但是部分不遵循这个规则?另外,我采用这种方法的原因是我想启用一些常见的渲染功能,而不需要每个控制器方法都改变它调用 View() 的方式。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-10-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-22
    • 1970-01-01
    • 2011-09-30
    相关资源
    最近更新 更多