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:
parent
abaf3bba50
commit
4212b7e713
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Reference in New Issue