diff --git a/src/Microsoft.AspNet.Mvc.Razor/RazorTextWriter.cs b/src/Microsoft.AspNet.Mvc.Razor/RazorTextWriter.cs index 3ca7aef74a..d9be07dbbf 100644 --- a/src/Microsoft.AspNet.Mvc.Razor/RazorTextWriter.cs +++ b/src/Microsoft.AspNet.Mvc.Razor/RazorTextWriter.cs @@ -8,6 +8,7 @@ using System.Text.Encodings.Web; using System.Threading.Tasks; using Microsoft.AspNet.Html; using Microsoft.AspNet.Mvc.ViewFeatures; +using Microsoft.AspNet.Mvc.ViewFeatures.Buffer; namespace Microsoft.AspNet.Mvc.Razor { @@ -24,11 +25,12 @@ namespace Microsoft.AspNet.Mvc.Razor /// /// The to write output to when this instance /// is no longer buffering. - /// The to buffer output to. + /// The to buffer output to. /// The HTML encoder. - public RazorTextWriter(TextWriter unbufferedWriter, IHtmlContentBuilder buffer, HtmlEncoder encoder) + public RazorTextWriter(TextWriter unbufferedWriter, ViewBuffer buffer, HtmlEncoder encoder) { UnbufferedWriter = unbufferedWriter; + Buffer = buffer; HtmlEncoder = encoder; BufferedWriter = new HtmlContentWrapperTextWriter(buffer, unbufferedWriter.Encoding); @@ -47,7 +49,7 @@ namespace Microsoft.AspNet.Mvc.Razor /// /// Gets the buffered content. /// - public IHtmlContent Buffer => BufferedWriter.ContentBuilder; + public ViewBuffer Buffer { get; } // Internal for unit testing internal HtmlContentWrapperTextWriter BufferedWriter { get; } diff --git a/src/Microsoft.AspNet.Mvc.Razor/RazorView.cs b/src/Microsoft.AspNet.Mvc.Razor/RazorView.cs index d2394a2a0d..81c6e82ecf 100644 --- a/src/Microsoft.AspNet.Mvc.Razor/RazorView.cs +++ b/src/Microsoft.AspNet.Mvc.Razor/RazorView.cs @@ -233,7 +233,7 @@ namespace Microsoft.AspNet.Mvc.Razor if (bodyWriter.IsBuffering) { // Only copy buffered content to the Output if we're currently buffering. - bodyWriter.Buffer.WriteTo(context.Writer, _htmlEncoder); + await bodyWriter.Buffer.WriteToAsync(context.Writer, _htmlEncoder); } } diff --git a/src/Microsoft.AspNet.Mvc.ViewFeatures/Buffer/ViewBuffer.cs b/src/Microsoft.AspNet.Mvc.ViewFeatures/Buffer/ViewBuffer.cs index a57fa7775e..7bc6f68656 100644 --- a/src/Microsoft.AspNet.Mvc.ViewFeatures/Buffer/ViewBuffer.cs +++ b/src/Microsoft.AspNet.Mvc.ViewFeatures/Buffer/ViewBuffer.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Text.Encodings.Web; +using System.Threading.Tasks; using Microsoft.AspNet.Html; using Microsoft.AspNet.Mvc.Rendering; @@ -142,7 +143,73 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures.Buffer for (var j = 0; j < count; j++) { var value = segment[j]; - value.WriteTo(writer, encoder); + + var valueAsString = value.Value as string; + if (valueAsString != null) + { + writer.Write(valueAsString); + continue; + } + + var valueAsHtmlContent = value.Value as IHtmlContent; + if (valueAsHtmlContent != null) + { + valueAsHtmlContent.WriteTo(writer, encoder); + continue; + } + } + } + } + + /// + /// Writes the buffered content to . + /// + /// The . + /// The . + /// A which will complete once content has been written. + public async Task WriteToAsync(TextWriter writer, HtmlEncoder encoder) + { + if (BufferSegments == null) + { + return; + } + + var htmlTextWriter = writer as HtmlTextWriter; + if (htmlTextWriter != null) + { + htmlTextWriter.Write(this); + return; + } + + for (var i = 0; i < BufferSegments.Count; i++) + { + var segment = BufferSegments[i]; + var count = i == BufferSegments.Count - 1 ? CurrentCount : segment.Length; + + for (var j = 0; j < count; j++) + { + var value = segment[j]; + + var valueAsString = value.Value as string; + if (valueAsString != null) + { + await writer.WriteAsync(valueAsString); + continue; + } + + var valueAsViewBuffer = value.Value as ViewBuffer; + if (valueAsViewBuffer != null) + { + await valueAsViewBuffer.WriteToAsync(writer, encoder); + continue; + } + + var valueAsHtmlContent = value.Value as IHtmlContent; + if (valueAsHtmlContent != null) + { + valueAsHtmlContent.WriteTo(writer, encoder); + continue; + } } } } diff --git a/src/Microsoft.AspNet.Mvc.ViewFeatures/Buffer/ViewBufferValue.cs b/src/Microsoft.AspNet.Mvc.ViewFeatures/Buffer/ViewBufferValue.cs index 7a29077b2c..e36b7ee0f6 100644 --- a/src/Microsoft.AspNet.Mvc.ViewFeatures/Buffer/ViewBufferValue.cs +++ b/src/Microsoft.AspNet.Mvc.ViewFeatures/Buffer/ViewBufferValue.cs @@ -35,31 +35,5 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures.Buffer /// Gets the value. /// public object Value { get; } - - /// - /// Writes the by encoding it with the specified to the - /// specified . - /// - /// The to write the value to. - /// The which encodes the content to be written. - public void WriteTo(TextWriter writer, HtmlEncoder encoder) - { - if (Value == null) - { - return; - } - - var stringValue = Value as string; - if (stringValue != null) - { - writer.Write(stringValue); - } - else - { - Debug.Assert(Value is IHtmlContent); - var htmlContentValue = (IHtmlContent)Value; - htmlContentValue.WriteTo(writer, encoder); - } - } } } diff --git a/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/Buffer/ViewBufferTest.cs b/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/Buffer/ViewBufferTest.cs index 979063dd89..df8eb4439d 100644 --- a/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/Buffer/ViewBufferTest.cs +++ b/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/Buffer/ViewBufferTest.cs @@ -3,6 +3,7 @@ using System.IO; using System.Linq; +using System.Threading.Tasks; using Microsoft.AspNet.Html; using Microsoft.AspNet.Mvc.Rendering; using Microsoft.AspNet.Mvc.ViewFeatures.Buffer; @@ -166,5 +167,63 @@ namespace Microsoft.AspNet.Mvc.Razor.Buffer // Assert Assert.Equal(expected, writer.ToString()); } + + [Fact] + public async Task WriteToAsync_WritesSelf_WhenWriterIsHtmlTextWriter() + { + // Arrange + var buffer = new ViewBuffer(new TestViewBufferScope(), "some-name"); + var htmlWriter = new Mock(); + htmlWriter.Setup(w => w.Write(buffer)).Verifiable(); + + // Act + buffer.Append("Hello world"); + await buffer.WriteToAsync(htmlWriter.Object, new HtmlTestEncoder()); + + // Assert + htmlWriter.Verify(); + } + + [Fact] + public async Task WriteToAsync_WritesRazorValues_ToTextWriter() + { + // Arrange + var buffer = new ViewBuffer(new TestViewBufferScope(), "some-name"); + var writer = new StringWriter(); + + // Act + buffer.Append("Hello"); + buffer.AppendHtml(new HtmlString(" world")); + buffer.AppendHtml(" 123"); + + await buffer.WriteToAsync(writer, new HtmlTestEncoder()); + + // Assert + Assert.Equal("Hello world 123", writer.ToString()); + } + + [Theory] + [InlineData(9)] + [InlineData(10)] + [InlineData(11)] + [InlineData(23)] + public async Task WriteToAsync_WritesRazorValuesFromAllBuffers(int valuesToWrite) + { + // Arrange + var buffer = new ViewBuffer(new TestViewBufferScope(4), "some-name"); + var writer = new StringWriter(); + var expected = string.Join("", Enumerable.Range(0, valuesToWrite).Select(_ => "abc")); + + // Act + for (var i = 0; i < valuesToWrite; i++) + { + buffer.AppendHtml("abc"); + } + + await buffer.WriteToAsync(writer, new HtmlTestEncoder()); + + // Assert + Assert.Equal(expected, writer.ToString()); + } } }