3

好的,我正在浏览 ILSpy 并试图弄清楚这里发生了什么,但我运气不佳。

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

通过从这个堆栈中推送和弹出,可以操纵给定 Razor 视图的输出。我希望通过 HTML 辅助扩展方法来利用它,但是我也注意到ViewContext,它也是HtmlHelper拥有自己的 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,实际上在 as 之后被倾倒<pre>Hello!</pre>。这既是预期的,也是期望的。
  • World!另一方面,立即输出,然后是一个空<pre></pre>块;换句话说,它没有捕获任何东西。

我的问题如下:这些TextWriter对象如何相互关联,为什么我不能ViewContext像从顶部一样 管理引用OutputStack?(相反,我如何管理引用ViewContext


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

  • Writer属性ViewContext不会丢弃传递给设置器的值,因此在第二个示例的情况下它不仅仅是删除它。
  • 沿着调用堆栈移动,OutputStack实际上来自PageContext.
  • ViewContext.Writer是在WebViewPage.ExecutePageHierarchy()使用WebViewPage属性中设置的Output也就是最上面的OutputStack!(只是我,还是这开始看起来像老鼠的依赖巢?
4

2 回答 2

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
}
于 2013-07-09T21:08:51.437 回答
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
于 2013-08-16T07:45:26.593 回答