RenderSection \ RenderSectionAsync does not work in sections
Fixes #1509
This commit is contained in:
parent
dd1500dcfb
commit
052ad3e95f
|
|
@ -451,7 +451,11 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
/// In layout pages, renders the content of the section named <paramref name="name"/>.
|
/// In layout pages, renders the content of the section named <paramref name="name"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="name">The name of the section to render.</param>
|
/// <param name="name">The name of the section to render.</param>
|
||||||
/// <returns>Returns a HtmlString that contains the rendered HTML.</returns>
|
/// <returns>Returns <see cref="HtmlString.Empty"/> to allow the <see cref="Write(object)"/> call to
|
||||||
|
/// succeed.</returns>
|
||||||
|
/// <remarks>The method writes to the <see cref="Output"/> and the value returned is a token
|
||||||
|
/// value that allows the Write (produced due to @RenderSection(..)) to succeed. However the
|
||||||
|
/// value does not represent the rendered content.</remarks>
|
||||||
public HtmlString RenderSection([NotNull] string name)
|
public HtmlString RenderSection([NotNull] string name)
|
||||||
{
|
{
|
||||||
return RenderSection(name, required: true);
|
return RenderSection(name, required: true);
|
||||||
|
|
@ -462,7 +466,11 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="name">The section to render.</param>
|
/// <param name="name">The section to render.</param>
|
||||||
/// <param name="required">Indicates if this section must be rendered.</param>
|
/// <param name="required">Indicates if this section must be rendered.</param>
|
||||||
/// <returns>Returns a HtmlString that contains the rendered HTML.</returns>
|
/// <returns>Returns <see cref="HtmlString.Empty"/> to allow the <see cref="Write(object)"/> call to
|
||||||
|
/// succeed.</returns>
|
||||||
|
/// <remarks>The method writes to the <see cref="Output"/> and the value returned is a token
|
||||||
|
/// value that allows the Write (produced due to @RenderSection(..)) to succeed. However the
|
||||||
|
/// value does not represent the rendered content.</remarks>
|
||||||
public HtmlString RenderSection([NotNull] string name, bool required)
|
public HtmlString RenderSection([NotNull] string name, bool required)
|
||||||
{
|
{
|
||||||
EnsureMethodCanBeInvoked(nameof(RenderSection));
|
EnsureMethodCanBeInvoked(nameof(RenderSection));
|
||||||
|
|
@ -475,8 +483,11 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
/// In layout pages, asynchronously renders the content of the section named <paramref name="name"/>.
|
/// In layout pages, asynchronously renders the content of the section named <paramref name="name"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="name">The section to render.</param>
|
/// <param name="name">The section to render.</param>
|
||||||
/// <returns>A <see cref="Task{TResult}"/> that on completion returns a <see cref="HtmlString"/> containing
|
/// <returns>A <see cref="Task{HtmlString}"/> that on completion returns <see cref="HtmlString.Empty"/> that
|
||||||
/// the rendered HTML.</returns>
|
/// allows the <see cref="Write(object)"/> call to succeed.</returns>
|
||||||
|
/// <remarks>The method writes to the <see cref="Output"/> and the value returned is a token
|
||||||
|
/// value that allows the Write (produced due to @RenderSection(..)) to succeed. However the
|
||||||
|
/// value does not represent the rendered content.</remarks>
|
||||||
public Task<HtmlString> RenderSectionAsync([NotNull] string name)
|
public Task<HtmlString> RenderSectionAsync([NotNull] string name)
|
||||||
{
|
{
|
||||||
return RenderSectionAsync(name, required: true);
|
return RenderSectionAsync(name, required: true);
|
||||||
|
|
@ -486,8 +497,11 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
/// In layout pages, asynchronously renders the content of the section named <paramref name="name"/>.
|
/// In layout pages, asynchronously renders the content of the section named <paramref name="name"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="name">The section to render.</param>
|
/// <param name="name">The section to render.</param>
|
||||||
/// <returns>A <see cref="Task{TResult}"/> that on completion returns a <see cref="HtmlString"/> containing
|
/// <returns>A <see cref="Task{HtmlString}"/> that on completion returns <see cref="HtmlString.Empty"/> that
|
||||||
/// the rendered HTML.</returns>
|
/// allows the <see cref="Write(object)"/> call to succeed.</returns>
|
||||||
|
/// <remarks>The method writes to the <see cref="Output"/> and the value returned is a token
|
||||||
|
/// value that allows the Write (produced due to @RenderSection(..)) to succeed. However the
|
||||||
|
/// value does not represent the rendered content.</remarks>
|
||||||
/// <exception cref="InvalidOperationException">if <paramref name="required"/> is <c>true</c> and the section
|
/// <exception cref="InvalidOperationException">if <paramref name="required"/> is <c>true</c> and the section
|
||||||
/// was not registered using the <c>@section</c> in the Razor page.</exception>
|
/// was not registered using the <c>@section</c> in the Razor page.</exception>
|
||||||
public async Task<HtmlString> RenderSectionAsync([NotNull] string name, bool required)
|
public async Task<HtmlString> RenderSectionAsync([NotNull] string name, bool required)
|
||||||
|
|
@ -508,14 +522,11 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
if (PreviousSectionWriters.TryGetValue(sectionName, out renderDelegate))
|
if (PreviousSectionWriters.TryGetValue(sectionName, out renderDelegate))
|
||||||
{
|
{
|
||||||
_renderedSections.Add(sectionName);
|
_renderedSections.Add(sectionName);
|
||||||
|
await renderDelegate(Output);
|
||||||
|
|
||||||
using (var writer = new StringCollectionTextWriter(Output.Encoding))
|
// Return a token value that allows the Write call that wraps the RenderSection \ RenderSectionAsync
|
||||||
{
|
// to succeed.
|
||||||
await renderDelegate(writer);
|
return HtmlString.Empty;
|
||||||
|
|
||||||
// Returning a disposed StringCollectionTextWriter is safe.
|
|
||||||
return new HtmlString(writer);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (required)
|
else if (required)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -67,8 +67,10 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
|
||||||
Assert.Equal("Final content", GetTrimmedString(stream));
|
Assert.Equal("Final content", GetTrimmedString(stream));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Theory]
|
||||||
public async Task FlushPointsAreExecutedForPagesWithComponentsAndPartials()
|
[InlineData("PageWithPartialsAndViewComponents", "FlushAsync invoked inside RenderSection")]
|
||||||
|
[InlineData("PageWithRenderSectionAsync", "FlushAsync invoked inside RenderSectionAsync")]
|
||||||
|
public async Task FlushPointsAreExecutedForPagesWithComponentsPartialsAndSections(string action, string title)
|
||||||
{
|
{
|
||||||
var waitService = new WaitService();
|
var waitService = new WaitService();
|
||||||
var serviceProvider = GetServiceProvider(waitService);
|
var serviceProvider = GetServiceProvider(waitService);
|
||||||
|
|
@ -77,23 +79,31 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
|
||||||
var client = server.CreateClient();
|
var client = server.CreateClient();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var stream = await client.GetStreamAsync("http://localhost/FlushPoint/PageWithPartialsAndViewComponents");
|
var stream = await client.GetStreamAsync("http://localhost/FlushPoint/" + action);
|
||||||
|
|
||||||
// Assert - 1
|
// Assert - 1
|
||||||
Assert.Equal(
|
Assert.Equal(string.Join(Environment.NewLine,
|
||||||
@"<title>Page With Components and Partials</title>
|
"<title>" + title + "</title>",
|
||||||
|
"",
|
||||||
RenderBody content", GetTrimmedString(stream));
|
"RenderBody content"), GetTrimmedString(stream));
|
||||||
waitService.WaitForServer();
|
waitService.WaitForServer();
|
||||||
|
|
||||||
// Assert - 2
|
// Assert - 2
|
||||||
Assert.Equal("partial-content", GetTrimmedString(stream));
|
Assert.Equal(string.Join(
|
||||||
|
Environment.NewLine,
|
||||||
|
"partial-content",
|
||||||
|
"",
|
||||||
|
"Value from TaskReturningString",
|
||||||
|
"<p>section-content</p>"), GetTrimmedString(stream));
|
||||||
waitService.WaitForServer();
|
waitService.WaitForServer();
|
||||||
|
|
||||||
// Assert - 3
|
// Assert - 3
|
||||||
Assert.Equal(
|
Assert.Equal(string.Join(
|
||||||
@"component-content
|
Environment.NewLine,
|
||||||
<span>Content that takes time to produce</span>", GetTrimmedString(stream));
|
"component-content",
|
||||||
|
" <span>Content that takes time to produce</span>",
|
||||||
|
"",
|
||||||
|
"More content from layout"), GetTrimmedString(stream));
|
||||||
}
|
}
|
||||||
|
|
||||||
private IServiceProvider GetServiceProvider(WaitService waitService)
|
private IServiceProvider GetServiceProvider(WaitService waitService)
|
||||||
|
|
|
||||||
|
|
@ -17,9 +17,16 @@ namespace RazorWebSite
|
||||||
return View();
|
return View();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This uses RenderSection to render the section that contains a FlushAsync call
|
||||||
public ViewResult PageWithPartialsAndViewComponents()
|
public ViewResult PageWithPartialsAndViewComponents()
|
||||||
{
|
{
|
||||||
return View();
|
return View();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This uses RenderSectionAsync to render the section that contains a FlushAsync call
|
||||||
|
public ViewResult PageWithRenderSectionAsync()
|
||||||
|
{
|
||||||
|
return View("PageWithSectionInvokedViaRenderSectionAsync");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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 System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace RazorWebSite
|
||||||
|
{
|
||||||
|
public class TaskReturningService
|
||||||
|
{
|
||||||
|
public async Task<string> GetValueAsync()
|
||||||
|
{
|
||||||
|
await Task.Delay(100);
|
||||||
|
return "Value from TaskReturningString";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -17,6 +17,7 @@ namespace RazorWebSite
|
||||||
// Add MVC services to the services container
|
// Add MVC services to the services container
|
||||||
services.AddMvc(configuration);
|
services.AddMvc(configuration);
|
||||||
services.AddTransient<InjectedHelper>();
|
services.AddTransient<InjectedHelper>();
|
||||||
|
services.AddTransient<TaskReturningService>();
|
||||||
services.Configure<RazorViewEngineOptions>(options =>
|
services.Configure<RazorViewEngineOptions>(options =>
|
||||||
{
|
{
|
||||||
var expander = new LanguageViewLocationExpander(
|
var expander = new LanguageViewLocationExpander(
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,19 @@
|
||||||
@inject WaitService WaitService
|
@inject WaitService WaitService
|
||||||
|
@inject TaskReturningService TaskReturningService
|
||||||
|
|
||||||
@{
|
@{
|
||||||
Layout = "/Views/Shared/_LayoutWithPartialAndFlush.cshtml";
|
Layout = "/Views/Shared/_LayoutWithPartialAndFlush.cshtml";
|
||||||
ViewBag.Title = "Page With Components and Partials";
|
ViewBag.Title = "FlushAsync invoked inside RenderSection";
|
||||||
}
|
}
|
||||||
RenderBody content
|
RenderBody content
|
||||||
@section content
|
@section content
|
||||||
{
|
{
|
||||||
|
@await TaskReturningService.GetValueAsync()
|
||||||
|
<p>section-content</p>
|
||||||
@{
|
@{
|
||||||
await FlushAsync();
|
await FlushAsync();
|
||||||
WaitService.WaitForClient();
|
WaitService.WaitForClient();
|
||||||
}
|
}
|
||||||
@await Component.InvokeAsync("ComponentThatSetsTitle")
|
@await Component.InvokeAsync("ComponentThatSetsTitle")
|
||||||
<span>Content that takes time to produce</span>
|
<span>Content that takes time to produce</span>
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
@inject WaitService WaitService
|
||||||
|
@inject TaskReturningService TaskReturningService
|
||||||
|
|
||||||
|
@{
|
||||||
|
Layout = "/Views/Shared/_LayoutWithRenderSectionAsync.cshtml";
|
||||||
|
ViewBag.Title = "FlushAsync invoked inside RenderSectionAsync";
|
||||||
|
}
|
||||||
|
RenderBody content
|
||||||
|
@section content
|
||||||
|
{
|
||||||
|
@await TaskReturningService.GetValueAsync()
|
||||||
|
<p>section-content</p>
|
||||||
|
@{
|
||||||
|
await FlushAsync();
|
||||||
|
WaitService.WaitForClient();
|
||||||
|
}
|
||||||
|
@await Component.InvokeAsync("ComponentThatSetsTitle")
|
||||||
|
<span>Content that takes time to produce</span>
|
||||||
|
}
|
||||||
|
|
@ -6,6 +6,3 @@
|
||||||
}
|
}
|
||||||
@RenderBody()
|
@RenderBody()
|
||||||
@await RenderSectionAsync("content")
|
@await RenderSectionAsync("content")
|
||||||
@{
|
|
||||||
WaitService.NotifyClient();
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,4 @@
|
||||||
}
|
}
|
||||||
@await Html.PartialAsync("_PartialThatSetsTitle")
|
@await Html.PartialAsync("_PartialThatSetsTitle")
|
||||||
@await RenderSectionAsync("content")
|
@await RenderSectionAsync("content")
|
||||||
@{
|
More content from layout
|
||||||
WaitService.NotifyClient();
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
@inject WaitService WaitService
|
||||||
|
<title>@ViewBag.Title</title>
|
||||||
|
@RenderBody()
|
||||||
|
@{
|
||||||
|
await FlushAsync();
|
||||||
|
WaitService.WaitForClient();
|
||||||
|
}
|
||||||
|
@await Html.PartialAsync("_PartialThatSetsTitle")
|
||||||
|
@await RenderSectionAsync("content")
|
||||||
|
More content from layout
|
||||||
Loading…
Reference in New Issue