TextWriter引用的行为不符合预期

时间:2013-07-09 20:36:29

标签: asp.net asp.net-mvc-4 razor output-buffering

好的,我正在和ILSpy一起巡游并试图弄清楚这里发生了什么,但我没有太多运气。

在ASP.NET MVC4应用程序的给定视图中(,当然这适用于MVC3,可能是2 )使用Razor引擎,OutputStack属性继承自{{ 3}}只是一堆WebPageBase个对象。

通过从该堆栈推送和弹出,可以操纵给定Razor视图的输出。我希望利用HTML帮助扩展方法来利用它,但我也注意到TextWriter,它也是ViewContext的公共成员它自己的TextWriter

现在,我进行了快速检查:(来自视图

@object.ReferenceEquals(this.ViewContext.Writer, this.OutputStack.Peek()) // True

但是,以下让我感到困惑:

@{
    // push one on
    OutputStack.Push(new StringWriter());
}
Hello!
@{
    // capture everything and pop it
    string buffer1 = OutputStack.Peek().ToString();
    OutputStack.Pop();
}
<pre>@(buffer1)</pre>

@{
    // apparently it references the top of the OutputStack
    // so this should be essentially the same
    var oldWriter = ViewContext.Writer;
    ViewContext.Writer = new StringWriter();
}
World!
@{
    // revert it and hope for the best
    string buffer2 = ViewContext.Writer.ToString();
    ViewContext.Writer = oldWriter;
}
<pre>@(buffer2)</pre>

以上结果如下:

  • Hello!被捕获到buffer1,并在<pre>Hello!</pre>之后被转储。这既是预期的,也是期望的。
  • 另一方面,
  • World!立即输出,然后是空<pre></pre>块;换句话说,它没有捕获任何东西。

我的问题如下:这些TextWriter个对象如何相互关联,为什么我无法通过ViewContext管理OutputStack的引用。{{1} }}? (相当,如何我可以通过ViewContext管理引用吗?


附录
我遇到过的详细信息和其他废话。

  • Writer的{​​{1}}属性不会丢弃传递给setter的值,因此在第二个示例的情况下,不只是丢弃它。
  • 移动到调用堆栈,ViewContext实际上来自HtmlHelper
  • OutputStack使用ViewContext.Writer属性WebViewPage.ExecutePageHierarchy(),{em>这是WebViewPage 的顶部,在Output中设置了! (只是我,或者这开始看起来像是老鼠的依赖巢?

2 个答案:

答案 0 :(得分:2)

我发表评论作为答案。它可能会注意到解决问题的学术部分,但它肯定可以解决问题的实际部分,最重要的是解决您试图解决的最初问题。

在您的助手中,您可以获得当前视图的OutputStack

public static void MyHelper(this HtmlHelper html)
{
    var stack = ((WebPageBase)html.ViewDataContainer).OutputStack;

    ... you could push and pop here and solve your real world problem
}

答案 1 :(得分:1)

根据我上面的评论,您似乎需要手动保持OutputStack.Peek()和ViewContext.Writer同步。以下辅助方法将执行此操作:

    private class WriterScope : IDisposable
    {
        private HtmlHelper _html;
        private TextWriter _previous;

        public WriterScope(HtmlHelper html, TextWriter writer)
        {
            _html = html;
            _previous = _html.ViewContext.Writer;
            _html.ViewContext.Writer = writer;
            ((WebPageBase)_html.ViewDataContainer).OutputStack.Push(writer);
        }

        public void Dispose()
        {
            var stack = ((WebPageBase) _html.ViewDataContainer).OutputStack;
            if (stack.Peek() == _html.ViewContext.Writer)
                _html.ViewContext.Writer = _previous;
            stack.Pop();
            _html = null;
            _previous = null;
        }
    }

    public static IDisposable Scope(this HtmlHelper html, TextWriter writer)
    {
        return new WriterScope(html, writer);
    }

在视图中,它可以像这样使用:

@{
    var sw = new StringWriter();
    using (Html.Scope(sw))
    {
        <p>Line 2</p>
        <p>@Html.Raw("Line 3")</p>
        <p>Line 4</p>
    }
}
<p>Line 1</p>
@Html.Raw(sw.ToString())

页面按预期输出以下内容:

Line 1
Line 2
Line 3
Line 4