I have a razor layout like:
@using (var context = SetUpSomeContext()) {
<div>
Some content here
@RenderBody();
</div>
}
And a view like:
@{
Layout = "MyLayout.cshtml";
}
<div>@SomethingThatDependsOnContextBeingSetUp()</div>
When the view renders, SomethingThatDependsOnContextBeingSetUp
executes before SetUpSomeContext
and fails. This seems weird, because I would expect that not to execute until RenderBody
is called in the layout. When I switch this to use a "PageContent" section instead of RenderBody, everything works as expected. Can anyone explain this behavior?
The Razor pipeline is:
First, Razor evaluates, if present, _ViewStart.cshtml that contains only Razor statements (C# or VB) for assign Layout or other initialization, it should never have html tags inside.
Then, it parse and evaluates the "View" cshtml file.
Then, it parse and evaluates, if present, the Layout, and when evaluates the @RenderBody
method of the cshtml layout file, replaces it with the html script resulting from evaluation of "View" cshtml file.
Finally, it builds the html control graph objects of layout and view html files.
So, you cannot do depend any "Razor" objects of a view from layout operations, but rather you may put in _ViewStart.cshtml your initialization of objects visible to your view.
You may imagine cs(vb)html views as a static content loaded when Controller.View
method is called.
At that point, the cshtml loaded content is parsed by Razor that evaluates the expressions (assign properties(as Layout), branchs, loops) and build a sort of binary tree or graph of "HtmlControls" objects into the ActionResult
object returned by View
method.
Next, ActionResult is rendered as html from Asp.Net and returned to the client as http response.
To do that, Razor parses cshtml files and carries out their code inside parts starting first from the "_ViewStart.cshtml" (also more of one if present in the sub folders chain related to the origin controller), then follows cshtml file loaded by conventions (name of view equals to the name of action in the path Views/[ControllerName]/), or by expressed view's name as parameter when calling View
method, and finally, the eventual layout file linked to the view by Layout
property.
Let me clarify this by investigating a situation,Assume that you have view like;
@renderSection("Header")
@using (var context = SetUpSomeContext()) {
<div>
Some content here
@RenderBody();
</div>
}
@renderSection("Footer")
And we are assuming that razor executes the page in the order you expect, what would happen if we declared our view like?
@{
Layout = null;
}
<div>@SomethingThatDependsOnContextBeingSetUp()</div>
Razor would not have any idea whether that view needs a layout page until executing @RenderBody().Also it would deduce that it rendered layout page for nothing and this would not be reasonable.So this is not what actually happens.
When request made it is so natural that Razor first executes body of your view.
If your view not specified layout like in my demo Razor only renders output of that page and stops there.If view has a layout specified like in your code
after executing the view , it passes control to layout page.(Layout page starts to render from top to bottom)So what is remaining for Layout page is only content placement.When it sees @RenderBody() it only places output of your already executed view.
For sections; they are not executed when your view body executed, after your view passes control to layout page,layout page explicitly invokes the execution of your sections in the order they are declared.
Also notice that you are specifying your page title in your view body and it is rendered in your layout title tag (ViewBag.Title).After executing view body all the variables which is declared in view body are available in layout page.
Sum:Rendering order is from top to bottom but execution order is different.
For your situation: "SomethingThatDependsOnContextBeingSetUp executes before SetUpSomeContext and fails". Like i said it is natural behaviour of Razor execution cycle, view body executed before layout page executed.When you make it section ; view body executed first but sections are not executed before layout page.View body passes control to layout page and Layout page starts to render from top to bottom and if it sees @RenderSection then invokes the execution of section.So in this case SetUpSomeContext is executed before SomethingThatDependsOnContextBeingSetUp executed.
The execution order is from innermost to outermost.
I would argue that using a 'context' the way you use it is not the best design - you should consider moving the setup to the controller / action filter and pass the data to the views in model.
If you require some logic on all your views, create a ViewModelBase
that all your ViewModel
s inherit from.
Then in your Controller(Base)
you can initialize the ViewModel.SharedContext
and other properties.