RazorView should not create new ViewContext when rendering pages.

For pages that can pop back (e.g. Partial pages \ View Components), the
invoking component already creates a new ViewContext. ViewStart, Pages
and Layouts need to share the same Layout. This is required for sharing
ViewData values (such as title).

Fixes #861
This commit is contained in:
Pranav K 2014-07-24 18:13:20 -07:00
parent 3746e44dc3
commit 92e26cf8e0
10 changed files with 67 additions and 10 deletions

View File

@ -88,12 +88,8 @@ namespace Microsoft.AspNet.Mvc.Razor
private async Task RenderPageCoreAsync(IRazorPage page, ViewContext context)
{
// Activating a page might mutate the ViewContext (for instance ViewContext.ViewData) is mutated by
// RazorPageActivator. We'll instead pass in a copy of the ViewContext.
var pageViewContext = new ViewContext(context, context.View, context.ViewData, context.Writer);
page.ViewContext = pageViewContext;
_pageActivator.Activate(page, pageViewContext);
page.ViewContext = context;
_pageActivator.Activate(page, context);
await page.ExecuteAsync();
}

View File

@ -85,5 +85,24 @@ test-value";
var body = await result.HttpContext.Response.ReadBodyAsStringAsync();
Assert.Equal(expected, body.Trim());
}
[Fact]
public async Task RazorView_PassesViewContextBetweenViewAndLayout()
{
var expected =
@"<title>Page title</title>
partial-content
component-content";
var server = TestServer.Create(_provider, _app);
var client = server.Handler;
// Act
var result = await client.GetAsync("http://localhost/ViewEngine/ViewPassesViewDataToLayout");
// Assert
var body = await result.HttpContext.Response.ReadBodyAsStringAsync();
Assert.Equal(expected, body.Trim());
}
}
}

View File

@ -42,7 +42,7 @@ namespace Microsoft.AspNet.Mvc.Razor
}
[Fact]
public async Task RenderAsync_WithoutHierarchy_ActivatesViews_WithACopyOfViewContext()
public async Task RenderAsync_WithoutHierarchy_ActivatesViews_WithThePassedInViewContext()
{
// Arrange
var viewData = new ViewDataDictionary(Mock.Of<IModelMetadataProvider>());
@ -59,12 +59,11 @@ namespace Microsoft.AspNet.Mvc.Razor
page,
executeViewHierarchy: false);
var viewContext = CreateViewContext(view);
var expectedViewData = viewContext.ViewData;
var expectedWriter = viewContext.Writer;
activator.Setup(a => a.Activate(page, It.IsAny<ViewContext>()))
.Callback((IRazorPage p, ViewContext c) =>
{
Assert.NotSame(c, viewContext);
Assert.Same(c, viewContext);
c.ViewData = viewData;
})
.Verifiable();
@ -74,7 +73,6 @@ namespace Microsoft.AspNet.Mvc.Razor
// Assert
activator.Verify();
Assert.Same(expectedViewData, viewContext.ViewData);
Assert.Same(expectedWriter, viewContext.Writer);
}

View File

@ -0,0 +1,16 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNet.Mvc;
namespace MvcSample.Web.Components
{
[ViewComponent(Name = "ComponentThatSetsTitle")]
public class ComponentThatSetsTitle : ViewComponent
{
public IViewComponentResult Invoke()
{
return View();
}
}
}

View File

@ -36,5 +36,11 @@ namespace RazorWebSite.Controllers
};
return View(model);
}
public ViewResult ViewPassesViewDataToLayout()
{
ViewData["Title"] = "Controller title";
return View("ViewWithTitle");
}
}
}

View File

@ -24,7 +24,11 @@
</PropertyGroup>
<ItemGroup>
<Content Include="Project.json" />
<Content Include="Views\Shared\Components\ComponentThatSetsTitle\Default.cshtml" />
<Content Include="Views\Shared\_LayoutWithTitle.cshtml" />
<Content Include="Views\Shared\_Partial.cshtml" />
<Content Include="Views\Shared\_PartialThatSetsTitle.cshtml" />
<Content Include="Views\ViewEngine\ViewWithTitle.cshtml" />
<Content Include="Views\ViewEngine\ViewWithNestedLayout.cshtml" />
<Content Include="Views\ViewEngine\ViewWithLayout.cshtml" />
<Content Include="Views\ViewEngine\ViewWithFullPath.cshtml" />
@ -34,6 +38,7 @@
<Content Include="Views\ViewEngine\_NestedLayout.cshtml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Components\ComponentThatSetsTitle.cs" />
<Compile Include="Controllers\ViewEngineController.cs" />
<Compile Include="Models\Person.cs" />
<Compile Include="Models\Address.cs" />

View File

@ -0,0 +1,4 @@
@{
ViewData["Title"] = "Component title";
}
component-content

View File

@ -0,0 +1,2 @@
<title>@ViewBag.Title</title>
@RenderBody()

View File

@ -0,0 +1,4 @@
@{
ViewBag.Title = "Partial title";
}
partial-content

View File

@ -0,0 +1,7 @@
@{
ViewData["Title"] = "Page title";
// The invoked partial sets a title, but this shouldn't override the current page's ViewData \ ViewBag.
await Html.RenderPartialAsync("_PartialThatSetsTitle");
await Component.RenderInvokeAsync("ComponentThatSetsTitle");
Layout = "/Views/Shared/_LayoutWithTitle.cshtml";
}