Add `SetOutputContentAsync` to `TagHelperExecutionContext` to prevent allocation of `Task<T>`.

- Previously we'd do `executionContext.Output.Content = await executionContext.Output.GetChildContentAsync()`. The problem with this approach is that it returned a `Task<TagHelperContent>` which could not be optimized when building in release.
- Added test to validate `SetOutputContentAsync` does the appropriate thing.

#721
This commit is contained in:
N. Taylor Mullen 2016-03-31 17:14:10 -07:00
parent abaf3bba50
commit 4212b7e713
19 changed files with 82 additions and 35 deletions

View File

@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.TagHelpers;
@ -243,6 +244,28 @@ namespace Microsoft.AspNetCore.Razor.Runtime.TagHelpers
Output.Reinitialize(tagName, tagMode);
}
/// <summary>
/// Executes children asynchronously with the page's <see cref="HtmlEncoder" /> in scope and
/// sets <see cref="Output"/>'s <see cref="TagHelperOutput.Content"/> to the rendered results.
/// </summary>
/// <returns>A <see cref="Task"/> that on completion sets <see cref="Output"/>'s
/// <see cref="TagHelperOutput.Content"/> to the children's rendered content.</returns>
public async Task SetOutputContentAsync()
{
var childContent = _childContent;
if (childContent == null)
{
_startTagHelperWritingScope(null);
await _executeChildContentAsync();
childContent = _endTagHelperWritingScope();
}
Debug.Assert(!Output.IsContentModified);
Output.Content.SetHtmlContent(childContent);
}
// Internal for testing.
internal async Task<TagHelperContent> GetChildContentAsync(bool useCachedResult, HtmlEncoder encoder)
{

View File

@ -583,13 +583,10 @@ namespace Microsoft.AspNetCore.Razor.CodeGenerators
using (_writer.BuildScope())
{
_writer
.Write(tagHelperOutputAccessor)
.Write(".")
.WriteStartAssignment(_tagHelperContext.TagHelperOutputContentPropertyName)
.Write("await ")
.WriteInstanceMethodInvocation(
tagHelperOutputAccessor,
_tagHelperContext.TagHelperOutputGetChildContentAsyncMethodName);
ExecutionContextVariableName,
_tagHelperContext.ExecutionContextSetOutputContentAsyncMethodName);
}
}

View File

@ -37,7 +37,7 @@ namespace Microsoft.AspNetCore.Razor.CodeGenerators
HtmlEncoderPropertyName = "HtmlEncoder";
TagHelperOutputIsContentModifiedPropertyName = "IsContentModified";
TagHelperOutputContentPropertyName = "Content";
TagHelperOutputGetChildContentAsyncMethodName = "GetChildContentAsync";
ExecutionContextSetOutputContentAsyncMethodName = "SetOutputContentAsync";
TagHelperAttributeTypeName = "Microsoft.AspNetCore.Razor.TagHelpers.TagHelperAttribute";
EncodedHtmlStringTypeName = "Microsoft.AspNetCore.Html.HtmlEncodedString";
}
@ -209,10 +209,10 @@ namespace Microsoft.AspNetCore.Razor.CodeGenerators
public string TagHelperOutputContentPropertyName { get; set; }
/// <summary>
/// The name of the method on the property <see cref="ExecutionContextOutputPropertyName"/> used to retrieve
/// tag helper child content.
/// The name of the method on the property <see cref="ExecutionContextOutputPropertyName"/> used to execute
/// child content and set the rendered results on its <see cref="ExecutionContextOutputPropertyName"/> property.
/// </summary>
public string TagHelperOutputGetChildContentAsyncMethodName { get; set; }
public string ExecutionContextSetOutputContentAsyncMethodName { get; set; }
/// <summary>
/// The name of the type used to represent tag helper attributes.

View File

@ -14,6 +14,33 @@ namespace Microsoft.AspNetCore.Razor.Runtime.TagHelpers
{
public class TagHelperExecutionContextTest
{
[Fact]
public async Task SetOutputContentAsync_SetsOutputsContent()
{
// Arrange
var tagHelperContent = new DefaultTagHelperContent();
var content = "Hello from child content";
var executionContext = new TagHelperExecutionContext(
"p",
tagMode: TagMode.StartTagAndEndTag,
items: new Dictionary<object, object>(),
uniqueId: string.Empty,
executeChildContentAsync: () =>
{
tagHelperContent.SetContent(content);
return Task.FromResult(result: true);
},
startTagHelperWritingScope: _ => { },
endTagHelperWritingScope: () => tagHelperContent);
// Act
await executionContext.SetOutputContentAsync();
// Assert
Assert.Equal(content, executionContext.Output.Content.GetContent());
}
[Fact]
public async Task ExecutionContext_Reinitialize_UpdatesTagHelperOutputAsExpected()
{

View File

@ -48,7 +48,7 @@ namespace TestOutput
await __tagHelperRunner.RunAsync(__tagHelperExecutionContext);
if (!__tagHelperExecutionContext.Output.IsContentModified)
{
__tagHelperExecutionContext.Output.Content = await __tagHelperExecutionContext.Output.GetChildContentAsync();
await __tagHelperExecutionContext.SetOutputContentAsync();
}
Instrumentation.BeginContext(54, 36, false);
Write(__tagHelperExecutionContext.Output);
@ -116,7 +116,7 @@ __TestNamespace_InputTagHelper2.Checked = true;
await __tagHelperRunner.RunAsync(__tagHelperExecutionContext);
if (!__tagHelperExecutionContext.Output.IsContentModified)
{
__tagHelperExecutionContext.Output.Content = await __tagHelperExecutionContext.Output.GetChildContentAsync();
await __tagHelperExecutionContext.SetOutputContentAsync();
}
Instrumentation.BeginContext(30, 228, false);
Write(__tagHelperExecutionContext.Output);

View File

@ -110,7 +110,7 @@ __TestNamespace_InputTagHelper2.Checked = **From custom attribute code renderer*
await __tagHelperRunner.RunAsync(__tagHelperExecutionContext);
if (!__tagHelperExecutionContext.Output.IsContentModified)
{
__tagHelperExecutionContext.Output.Content = await __tagHelperExecutionContext.Output.GetChildContentAsync();
await __tagHelperExecutionContext.SetOutputContentAsync();
}
Instrumentation.BeginContext(104, 216, false);
Write(__tagHelperExecutionContext.Output);

View File

@ -66,7 +66,7 @@ namespace TestOutput
await __tagHelperRunner.RunAsync(__tagHelperExecutionContext);
if (!__tagHelperExecutionContext.Output.IsContentModified)
{
__tagHelperExecutionContext.Output.Content = await __tagHelperExecutionContext.Output.GetChildContentAsync();
await __tagHelperExecutionContext.SetOutputContentAsync();
}
Instrumentation.BeginContext(107, 136, false);
Write(__tagHelperExecutionContext.Output);

View File

@ -111,7 +111,7 @@ __TestNamespace_InputTagHelper2.Checked = true;
await __tagHelperRunner.RunAsync(__tagHelperExecutionContext);
if (!__tagHelperExecutionContext.Output.IsContentModified)
{
__tagHelperExecutionContext.Output.Content = await __tagHelperExecutionContext.Output.GetChildContentAsync();
await __tagHelperExecutionContext.SetOutputContentAsync();
}
Instrumentation.BeginContext(104, 216, false);
Write(__tagHelperExecutionContext.Output);

View File

@ -96,7 +96,7 @@ namespace TestOutput
await __tagHelperRunner.RunAsync(__tagHelperExecutionContext);
if (!__tagHelperExecutionContext.Output.IsContentModified)
{
__tagHelperExecutionContext.Output.Content = await __tagHelperExecutionContext.Output.GetChildContentAsync();
await __tagHelperExecutionContext.SetOutputContentAsync();
}
Instrumentation.BeginContext(265, 83, false);
Write(__tagHelperExecutionContext.Output);
@ -155,7 +155,7 @@ namespace TestOutput
await __tagHelperRunner.RunAsync(__tagHelperExecutionContext);
if (!__tagHelperExecutionContext.Output.IsContentModified)
{
__tagHelperExecutionContext.Output.Content = await __tagHelperExecutionContext.Output.GetChildContentAsync();
await __tagHelperExecutionContext.SetOutputContentAsync();
}
Instrumentation.BeginContext(414, 58, false);
Write(__tagHelperExecutionContext.Output);
@ -266,7 +266,7 @@ AddHtmlAttributeValue(" ", 159, DateTime.Now, 160, 14, false);
await __tagHelperRunner.RunAsync(__tagHelperExecutionContext);
if (!__tagHelperExecutionContext.Output.IsContentModified)
{
__tagHelperExecutionContext.Output.Content = await __tagHelperExecutionContext.Output.GetChildContentAsync();
await __tagHelperExecutionContext.SetOutputContentAsync();
}
Instrumentation.BeginContext(137, 529, false);
Write(__tagHelperExecutionContext.Output);
@ -330,7 +330,7 @@ __TestNamespace_InputTagHelper2.Checked = (@object);
await __tagHelperRunner.RunAsync(__tagHelperExecutionContext);
if (!__tagHelperExecutionContext.Output.IsContentModified)
{
__tagHelperExecutionContext.Output.Content = await __tagHelperExecutionContext.Output.GetChildContentAsync();
await __tagHelperExecutionContext.SetOutputContentAsync();
}
Instrumentation.BeginContext(678, 181, false);
Write(__tagHelperExecutionContext.Output);
@ -379,7 +379,7 @@ __TestNamespace_PTagHelper.Age = -1970 + @DateTimeOffset.Now.Year;
await __tagHelperRunner.RunAsync(__tagHelperExecutionContext);
if (!__tagHelperExecutionContext.Output.IsContentModified)
{
__tagHelperExecutionContext.Output.Content = await __tagHelperExecutionContext.Output.GetChildContentAsync();
await __tagHelperExecutionContext.SetOutputContentAsync();
}
Instrumentation.BeginContext(869, 155, false);
Write(__tagHelperExecutionContext.Output);
@ -426,7 +426,7 @@ __TestNamespace_PTagHelper.Age = DateTimeOffset.Now.Year - 1970;
await __tagHelperRunner.RunAsync(__tagHelperExecutionContext);
if (!__tagHelperExecutionContext.Output.IsContentModified)
{
__tagHelperExecutionContext.Output.Content = await __tagHelperExecutionContext.Output.GetChildContentAsync();
await __tagHelperExecutionContext.SetOutputContentAsync();
}
Instrumentation.BeginContext(1034, 116, false);
Write(__tagHelperExecutionContext.Output);
@ -473,7 +473,7 @@ __TestNamespace_PTagHelper.Age = ("My age is this long.".Length);
await __tagHelperRunner.RunAsync(__tagHelperExecutionContext);
if (!__tagHelperExecutionContext.Output.IsContentModified)
{
__tagHelperExecutionContext.Output.Content = await __tagHelperExecutionContext.Output.GetChildContentAsync();
await __tagHelperExecutionContext.SetOutputContentAsync();
}
Instrumentation.BeginContext(1160, 133, false);
Write(__tagHelperExecutionContext.Output);
@ -518,7 +518,7 @@ __TestNamespace_PTagHelper.Age = 123;
await __tagHelperRunner.RunAsync(__tagHelperExecutionContext);
if (!__tagHelperExecutionContext.Output.IsContentModified)
{
__tagHelperExecutionContext.Output.Content = await __tagHelperExecutionContext.Output.GetChildContentAsync();
await __tagHelperExecutionContext.SetOutputContentAsync();
}
Instrumentation.BeginContext(1316, 57, false);
WriteTo(__razor_template_writer, __tagHelperExecutionContext.Output);

View File

@ -98,7 +98,7 @@ __TestNamespace_PTagHelper.Age = 3;
await __tagHelperRunner.RunAsync(__tagHelperExecutionContext);
if (!__tagHelperExecutionContext.Output.IsContentModified)
{
__tagHelperExecutionContext.Output.Content = await __tagHelperExecutionContext.Output.GetChildContentAsync();
await __tagHelperExecutionContext.SetOutputContentAsync();
}
Instrumentation.BeginContext(33, 157, false);
Write(__tagHelperExecutionContext.Output);

View File

@ -97,7 +97,7 @@ __TestNamespace_PTagHelper.Age = ;
await __tagHelperRunner.RunAsync(__tagHelperExecutionContext);
if (!__tagHelperExecutionContext.Output.IsContentModified)
{
__tagHelperExecutionContext.Output.Content = await __tagHelperExecutionContext.Output.GetChildContentAsync();
await __tagHelperExecutionContext.SetOutputContentAsync();
}
Instrumentation.BeginContext(78, 64, false);
Write(__tagHelperExecutionContext.Output);

View File

@ -123,7 +123,7 @@ namespace TestOutput
await __tagHelperRunner.RunAsync(__tagHelperExecutionContext);
if (!__tagHelperExecutionContext.Output.IsContentModified)
{
__tagHelperExecutionContext.Output.Content = await __tagHelperExecutionContext.Output.GetChildContentAsync();
await __tagHelperExecutionContext.SetOutputContentAsync();
}
Instrumentation.BeginContext(33, 647, false);
Write(__tagHelperExecutionContext.Output);

View File

@ -102,7 +102,7 @@ namespace TestOutput
await __tagHelperRunner.RunAsync(__tagHelperExecutionContext);
if (!__tagHelperExecutionContext.Output.IsContentModified)
{
__tagHelperExecutionContext.Output.Content = await __tagHelperExecutionContext.Output.GetChildContentAsync();
await __tagHelperExecutionContext.SetOutputContentAsync();
}
Instrumentation.BeginContext(137, 433, false);
Write(__tagHelperExecutionContext.Output);

View File

@ -46,7 +46,7 @@ __TestNamespace_PTagHelper.Age = 1337;
await __tagHelperRunner.RunAsync(__tagHelperExecutionContext);
if (!__tagHelperExecutionContext.Output.IsContentModified)
{
__tagHelperExecutionContext.Output.Content = await __tagHelperExecutionContext.Output.GetChildContentAsync();
await __tagHelperExecutionContext.SetOutputContentAsync();
}
Instrumentation.BeginContext(33, 49, false);
Write(__tagHelperExecutionContext.Output);

View File

@ -46,7 +46,7 @@ __TestNamespace_PTagHelper.Age = 1337;
await __tagHelperRunner.RunAsync(__tagHelperExecutionContext);
if (!__tagHelperExecutionContext.Output.IsContentModified)
{
__tagHelperExecutionContext.Output.Content = await __tagHelperExecutionContext.Output.GetChildContentAsync();
await __tagHelperExecutionContext.SetOutputContentAsync();
}
Instrumentation.BeginContext(33, 53, false);
Write(__tagHelperExecutionContext.Output);

View File

@ -96,7 +96,7 @@ __TestNamespace_CatchAllTagHelper.Event1 = doSomething();
await __tagHelperRunner.RunAsync(__tagHelperExecutionContext);
if (!__tagHelperExecutionContext.Output.IsContentModified)
{
__tagHelperExecutionContext.Output.Content = await __tagHelperExecutionContext.Output.GetChildContentAsync();
await __tagHelperExecutionContext.SetOutputContentAsync();
}
Instrumentation.BeginContext(374, 79, false);
Write(__tagHelperExecutionContext.Output);
@ -124,7 +124,7 @@ __TestNamespace_CatchAllTagHelper.Event2 = doSomething();
await __tagHelperRunner.RunAsync(__tagHelperExecutionContext);
if (!__tagHelperExecutionContext.Output.IsContentModified)
{
__tagHelperExecutionContext.Output.Content = await __tagHelperExecutionContext.Output.GetChildContentAsync();
await __tagHelperExecutionContext.SetOutputContentAsync();
}
Instrumentation.BeginContext(455, 81, false);
Write(__tagHelperExecutionContext.Output);
@ -148,7 +148,7 @@ __TestNamespace_CatchAllTagHelper.Event2 = doSomething();
await __tagHelperRunner.RunAsync(__tagHelperExecutionContext);
if (!__tagHelperExecutionContext.Output.IsContentModified)
{
__tagHelperExecutionContext.Output.Content = await __tagHelperExecutionContext.Output.GetChildContentAsync();
await __tagHelperExecutionContext.SetOutputContentAsync();
}
Instrumentation.BeginContext(538, 67, false);
Write(__tagHelperExecutionContext.Output);

View File

@ -64,7 +64,7 @@ namespace TestOutput
await __tagHelperRunner.RunAsync(__tagHelperExecutionContext);
if (!__tagHelperExecutionContext.Output.IsContentModified)
{
__tagHelperExecutionContext.Output.Content = await __tagHelperExecutionContext.Output.GetChildContentAsync();
await __tagHelperExecutionContext.SetOutputContentAsync();
}
Instrumentation.BeginContext(267, 66, false);
Write(__tagHelperExecutionContext.Output);
@ -99,7 +99,7 @@ AddHtmlAttributeValue(" ", 199, DateTime.Now, 200, 14, false);
await __tagHelperRunner.RunAsync(__tagHelperExecutionContext);
if (!__tagHelperExecutionContext.Output.IsContentModified)
{
__tagHelperExecutionContext.Output.Content = await __tagHelperExecutionContext.Output.GetChildContentAsync();
await __tagHelperExecutionContext.SetOutputContentAsync();
}
Instrumentation.BeginContext(112, 245, false);
WriteTo(__razor_section_writer, __tagHelperExecutionContext.Output);

View File

@ -59,7 +59,7 @@ __TestNamespace_PTagHelper.Age = 1337;
await __tagHelperRunner.RunAsync(__tagHelperExecutionContext);
if (!__tagHelperExecutionContext.Output.IsContentModified)
{
__tagHelperExecutionContext.Output.Content = await __tagHelperExecutionContext.Output.GetChildContentAsync();
await __tagHelperExecutionContext.SetOutputContentAsync();
}
Instrumentation.BeginContext(33, 85, false);
Write(__tagHelperExecutionContext.Output);

View File

@ -58,7 +58,7 @@ __TestNamespace_PTagHelper.Age = 1337;
await __tagHelperRunner.RunAsync(__tagHelperExecutionContext);
if (!__tagHelperExecutionContext.Output.IsContentModified)
{
__tagHelperExecutionContext.Output.Content = await __tagHelperExecutionContext.Output.GetChildContentAsync();
await __tagHelperExecutionContext.SetOutputContentAsync();
}
Instrumentation.BeginContext(97, 44, false);
Write(__tagHelperExecutionContext.Output);