24

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

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

像这样的视图:

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

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

4

4 回答 4

21

Razor 管道是:

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

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

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

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


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


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

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

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

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

于 2016-08-09T14:16:18.900 回答
8

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

 @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)中呈现。执行视图正文后,在视图正文中声明的所有变量都可以在布局页面中使用。

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

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

于 2015-07-04T01:02:37.993 回答
4

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

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

于 2013-08-13T13:50:27.653 回答
1

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

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

于 2013-08-13T14:17:47.843 回答