The ways I found are these, from the "best" to the worst. full code here
Initial code
This only works with .NET Framework.
/// <summary>
/// The old way.
/// </summary>
[Fact]
public void Test0()
{
var contents = new StringBuilder();
var tag = new TagBuilder("div");
tag.SetInnerText("hello"); // method does not exist
contents.Append(tag.ToString(TagRenderMode.StartTag)); // missing method overload
Assert.Equal("<div>", contents.ToString());
contents.Append(tag.InnerHtml);
Assert.Equal("<div>hello", contents.ToString());
contents.Append(tag.ToString(TagRenderMode.EndTag)); // missing method overload
Assert.Equal("<div>hello</div>", contents.ToString());
var result = MvcHtmlString.Create(contents.ToString()); // class does not exist
Assert.Equal("<div>hello</div>", result.ToString());
}
Minimal changes
This method focuses on minimal changes. You only need to change one type and declare a 3 extension methods.
/// <summary>
/// Minimal code changes: extension methods.
/// </summary>
[Fact]
public void Test4()
{
var contents = new StringWriter(); // change this type
var tag = new TagBuilder("div");
tag.SetInnerText("hello"); // 1 extension method
contents.Append(tag.ToString(TagRenderMode.StartTag)); // 2 extension methods
Assert.Equal("<div>", contents.ToString());
contents.Append(tag.InnerHtml); // 1 extension method
Assert.Equal("<div>hello", contents.ToString());
contents.Append(tag.ToString(TagRenderMode.EndTag)); // 2 extension methods
Assert.Equal("<div>hello</div>", contents.ToString());
}
public static void SetInnerText(this TagBuilder tag, string value)
{
tag.InnerHtml.Append(value);
}
public static void Append(this StringWriter writer, IHtmlContent html)
{
html.WriteTo(writer, HtmlEncoder.Default);
}
public static IHtmlContent ToString(this TagBuilder tag, TagRenderMode mode)
{
if (mode == TagRenderMode.StartTag)
{
return tag.RenderStartTag();
}
else if (mode == TagRenderMode.EndTag)
{
return tag.RenderEndTag();
}
else
{
throw new ArgumentException();
}
}
Minimal call stack
Here a focus on not using extra methods; code changes a lot.
/// <summary>
/// Use StringWriter instead of StringBuilder, no ext. methods.
/// </summary>
[Fact]
public void Test3()
{
var contents = new StringWriter(); // changed
var tag = new TagBuilder("div");
tag.InnerHtml.Append("hello");
tag.RenderStartTag().WriteTo(contents, HtmlEncoder.Default); // changed
Assert.Equal("<div>", contents.ToString());
tag.RenderBody()?.WriteTo(contents, HtmlEncoder.Default); // changed
Assert.Equal("<div>hello", contents.ToString());
tag.RenderEndTag().WriteTo(contents, HtmlEncoder.Default); // changed
Assert.Equal("<div>hello</div>", contents.ToString());
}
The memory hogger
There is a known extension method that will allocate too much memory. Don't do this one.
/// <summary>
/// Use Render methods and bad extension methods.
/// </summary>
/// <remarks>So many StringWriters</remarks>
[Fact]
public void Test2()
{
var contents = new StringBuilder();
var tag = new TagBuilder("div");
tag.InnerHtml.Append("hello");
contents.AppendEx(tag.RenderStartTag());
Assert.Equal("<div>", contents.ToString());
contents.AppendEx(tag.RenderBody());
Assert.Equal("<div>hello", contents.ToString());
contents.AppendEx(tag.RenderEndTag());
Assert.Equal("<div>hello</div>", contents.ToString());
}
public static void AppendEx(this StringBuilder builder, IHtmlContent? content)
{
if (content != null)
{
using (var writer = new StringWriter(builder))
{
content.WriteTo(writer, HtmlEncoder.Default);
}
}
}
Here is a variant using TagRenderMode.
/// <summary>
/// Set RenderMode and use extension methods.
/// </summary>
/// <remarks>So many StringWriters</remarks>
[Fact]
public void Test1()
{
var contents = new StringBuilder();
var tag = new TagBuilder("div");
tag.InnerHtml.Append("hello");
tag.TagRenderMode = TagRenderMode.StartTag;
contents.AppendEx(tag);
Assert.Equal("<div>", contents.ToString());
contents.AppendEx(tag.InnerHtml);
Assert.Equal("<div>hello", contents.ToString());
tag.TagRenderMode = TagRenderMode.EndTag;
contents.AppendEx(tag);
Assert.Equal("<div>hello</div>", contents.ToString());
}
public static void AppendEx(this StringBuilder contents, TagBuilder tag)
{
// why do we need to do that? my StringBuilder is no good enough?
using (var writer = new StringWriter(contents))
{
tag.WriteTo(writer, HtmlEncoder.Default);
}
}