I'm trying to make an HTML helper that will allow for any text inside a using to be consumed and not written to the final page.
The Razor:
<div>
<p>
Before Inline
</p>
@using (Html.IncludeInlineScript("testScript"))
{
<script type="text/javascript">
alert("hello world");
</script>
}
<p>
After Inline
</p>
</div>
The resulting HTML:
<div>
<p>
Before Inline
</p>
<script type="text/javascript">
alert("hello world");
</script>
<!-- Top of Dispose -->
<!-- Bottom of Dispose -->
<p>
After Inline
</p>
</div>
The Helper Extension Method:
public static ScriptWrapper IncludeInlineScript(this HtmlHelper helper, string scriptName)
{
return new ScriptWrapper(helper);
}
The Wrapper:
public class ScriptWrapper : IDisposable
{
private HtmlHelper _helper;
private TextWriter _originalWriter;
private StringBuilder _scriptContents;
public ScriptWrapper(HtmlHelper helper)
{
_helper = helper;
_originalWriter = _helper.ViewContext.Writer;
_scriptContents = new StringBuilder();
_helper.ViewContext.Writer = new StringWriter(_scriptContents);
}
public void Dispose()
{
_originalWriter.WriteLine("<!-- Top of Dispose -->");
_helper.ViewContext.Writer.Flush();
_helper.ViewContext.Writer = _originalWriter;
_originalWriter.WriteLine("<!-- Bottom of Dispose -->");
}
}
The problem here is that despite setting the ViewContext.Writer
to a new TextWriter it still is writing to the original writer. Clearly the dispose is being invoked in the right order as the top of the dispose is after the script. While debugging after the new writer has been set the <script>
block is not in the stream, however at the time of the dispose the <script>
is now contained in the original writer.
Is the razor engine keeping a local copy of the writer and ignores the fact that it had been set to a different instance? This seems like a bug to me.
In case anyone wants a work around, since I'm impatient, I found a way to make this work. It's less than ideal, but it does the trick. If you modify the ScriptWrapper
to be:
public class ScriptWrapper : IDisposable
{
private HtmlHelper _helper;
private string _originalString;
private StringBuilder _sb;
public ScriptWrapper(HtmlHelper helper)
{
_helper = helper;
_sb = ((StringWriter) _helper.ViewContext.Writer).GetStringBuilder();
_originalString = _sb.ToString();
_sb.Clear();
}
public void Dispose()
{
var contents = _sb.ToString();
_sb.Clear();
_sb.Append(_originalString);
}
}
You can accomplish the desired result of consuming anything inside the using(...)
statement.
I would however still be curious to know if there is a way to do it without hacking a solution like this.