Flatten TagHelperOutput when writing
This change 'flattens' a TagHelperOutput when writing it out to an HtmlTextWriter. This is a beneficial perf change because more often than not the thing being written to is a ViewBuffer in Razor, which uses pooled memory. This allows us to 'join' islands of pooled ViewBuffer memory back into the main buffer instead of keeping them wrapped up in a TagHelperOutput or TagHelperContent.
This commit is contained in:
parent
2e7e1f9c85
commit
18909506e1
|
|
@ -283,14 +283,14 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
|
|||
|
||||
if (!isTagNameNullOrWhitespace)
|
||||
{
|
||||
writer.Write('<');
|
||||
writer.Write("<");
|
||||
writer.Write(TagName);
|
||||
|
||||
// Perf: Avoid allocating enumerator
|
||||
for (var i = 0; i < Attributes.Count; i++)
|
||||
{
|
||||
var attribute = Attributes[i];
|
||||
writer.Write(' ');
|
||||
writer.Write(" ");
|
||||
writer.Write(attribute.Name);
|
||||
|
||||
if (attribute.Minimized)
|
||||
|
|
@ -309,10 +309,9 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
|
|||
using (var stringWriter = new StringWriter())
|
||||
{
|
||||
htmlContent.WriteTo(stringWriter, encoder);
|
||||
stringWriter.GetStringBuilder().Replace("\"", """);
|
||||
|
||||
var stringValue = stringWriter.ToString();
|
||||
stringValue = stringValue.Replace("\"", """);
|
||||
|
||||
writer.Write(stringValue);
|
||||
}
|
||||
}
|
||||
|
|
@ -321,7 +320,7 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
|
|||
encoder.Encode(writer, value.ToString());
|
||||
}
|
||||
|
||||
writer.Write('"');
|
||||
writer.Write("\"");
|
||||
}
|
||||
|
||||
if (TagMode == TagMode.SelfClosing)
|
||||
|
|
@ -329,7 +328,7 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
|
|||
writer.Write(" /");
|
||||
}
|
||||
|
||||
writer.Write('>');
|
||||
writer.Write(">");
|
||||
}
|
||||
|
||||
if (isTagNameNullOrWhitespace || TagMode == TagMode.StartTagAndEndTag)
|
||||
|
|
|
|||
|
|
@ -4,8 +4,10 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Text.Encodings.Web;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Html;
|
||||
using Microsoft.AspNetCore.Razor.Runtime.TagHelpers;
|
||||
using Microsoft.Extensions.WebEncoders.Testing;
|
||||
using Xunit;
|
||||
|
|
@ -990,6 +992,34 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
|
|||
Assert.Equal(expected, writer.ToString(), StringComparer.Ordinal);
|
||||
}
|
||||
|
||||
// This tests a separate code path that's used by THO when the writer is an HtmlTextWriter.
|
||||
// The output should be the same, but we do some specific perf optimizations on this path.
|
||||
[Theory]
|
||||
[MemberData(nameof(WriteTagHelper_InputData))]
|
||||
public void WriteTo_WritesFormattedTagHelper_HtmlTextWriter(TagHelperOutput output, string expected)
|
||||
{
|
||||
// Arrange
|
||||
var inner = new StringWriter();
|
||||
var testEncoder = new HtmlTestEncoder();
|
||||
var writer = new MockHtmlTextWriter(inner, testEncoder);
|
||||
|
||||
var tagHelperExecutionContext = new TagHelperExecutionContext(
|
||||
tagName: output.TagName,
|
||||
tagMode: output.TagMode,
|
||||
items: new Dictionary<object, object>(),
|
||||
uniqueId: string.Empty,
|
||||
executeChildContentAsync: () => Task.FromResult(result: true),
|
||||
startTagHelperWritingScope: _ => { },
|
||||
endTagHelperWritingScope: () => new DefaultTagHelperContent());
|
||||
tagHelperExecutionContext.Output = output;
|
||||
|
||||
// Act
|
||||
output.WriteTo(writer, testEncoder);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expected, inner.ToString(), StringComparer.Ordinal);
|
||||
}
|
||||
|
||||
private static TagHelperOutput GetTagHelperOutput(
|
||||
string tagName,
|
||||
TagHelperAttributeList attributes,
|
||||
|
|
@ -1036,5 +1066,29 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
|
|||
|
||||
return output;
|
||||
}
|
||||
|
||||
private class MockHtmlTextWriter : HtmlTextWriter
|
||||
{
|
||||
private readonly HtmlEncoder _encoder;
|
||||
private readonly TextWriter _inner;
|
||||
|
||||
public MockHtmlTextWriter(TextWriter inner, HtmlEncoder encoder)
|
||||
{
|
||||
_inner = inner;
|
||||
_encoder = encoder;
|
||||
}
|
||||
|
||||
public override Encoding Encoding => _inner.Encoding;
|
||||
|
||||
public override void Write(IHtmlContent value)
|
||||
{
|
||||
value.WriteTo(_inner, _encoder);
|
||||
}
|
||||
|
||||
public override void Write(char value)
|
||||
{
|
||||
_inner.Write(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue