详解WebFormView中标准修改办法

开发 后端
这里将介绍WebFormView中标准修改办法及MvcPatch项目,这些都与Ajax有关,希望本文能对大家有关。

对于WebFormView的标准修改办法以及MvcPatch项目,这些东西的好处是如果我们在构建一个面向AJAX请求的Action,此时View的内容可能只是输出的一部分,甚至我们要对内容进行过滤/编码等额外操作。

浅析WebFormView中的一个Bug中我提到WebFormView的实现破坏了IView对象设计思路,它会把视图内容直接生成至HttpContext.Current而不是Render方法指定的TextWriter中。目前,WebFormView.Render的调用方只有两个:ViewResult.ExecuteResult方法还有HtmlHelper.RenderPartial方法,但是这两者原本的目的地就是当前的HttpContext,因此在平时使用时WebFormView的错误实现并不会造成问题。

  1. public static class HtmlExtensions  
  2. {  
  3. public static string Partial(this HtmlHelper htmlHelper, string partial)  
  4. {  
  5. var viewInstance = BuildManager.CreateInstanceFromVirtualPath(partial, typeof(object));  
  6. var control = viewInstance as ViewUserControl;  
  7.  
  8. control.ViewContext = htmlHelper.ViewContext;  
  9. control.ViewData = htmlHelper.ViewData;  
  10.  
  11. Page page = new ViewPage();  
  12. page.Controls.Add(control);  
  13.  
  14. TextWriter writer = new StringWriter();  
  15. htmlHelper.ViewContext.HttpContext.Server.Execute(page, writer, false);  
  16.  
  17. return writer.ToString();  
  18. }  

但是,如果我们在构建一个面向AJAX请求的Action,此时View的内容可能只是输出的一部分,甚至我们要对内容进行过滤/编码等额外操作。此时,我们就希望指定一个TextWriter用于收集内容——但是WebFormView自然无法做到。之前我提出了一种非常临时,非常山寨,非常简陋,绕弯,但是可行,或者说是可以“表现出解决问题的方法”的代码,修改一下便能说明问题:

这个HtmlHelper的扩展方法Partial,和HtmlHelper自带的RenderPartial功能比较接近,不过Partial是将视图内容直接生成一个字符串并返回,RenderPartial方法是直接输出至当前HttpContext。因此它们在视图中的使用方式是不同的:

<% Html.RenderPartial("MyPartialView"); %>
<%= Html.Partial("MyPartialView") %>RenderPartial以<%开头,末尾有分号。而Partial以<%=开头,末尾没有分号。关于视图中的各种输出方式,我最近在阅读ASP.NET源代码时有更深的了解,下次我们再详谈。不过目前,我们还是专注于WebFormView的修改。

WebFormView目前问题的主要原因,是ViewPage和ViewUserControl两个类中缺乏合适接口的原因:

  1. public class ViewPage : Page, IViewDataContainer {  
  2. ...  
  3. public virtual void RenderView(ViewContext viewContext) {  
  4. ViewContext = viewContext;  
  5. InitHelpers();  
  6. // Tracing requires Page IDs to be unique.  
  7. ID = Guid.NewGuid().ToString();  
  8. ProcessRequest(HttpContext.Current);  
  9. }  
  10. }  
  11.  
  12. public class ViewUserControl : UserControl, IViewDataContainer {  
  13. ...  
  14. public virtual void RenderView(ViewContext viewContext) {  
  15. viewContext.HttpContext.Response.Cache.SetExpires(DateTime.Now);  
  16. // 这是ViewPage的子类,专用于生成独立的ViewUserControl内容  
  17. var containerPage = new ViewUserControlContainerPage(this);  
  18. // Tracing requires Page IDs to be unique.  
  19. ID = Guid.NewGuid().ToString();  
  20.  
  21. // 其中会执行ViewUserControlContrainerPage的RenderView方法  
  22. RenderViewAndRestoreContentType(containerPage, viewContext);  
  23. }  

可见,在ViewPage和ViewUserControl中各有一个RenderView方法,它们只包含一个ViewContext参数,但是却没有输出目的地。因此,最终它们使用HttpContext.Current这个邪恶的、臭名昭著的静态属性来生成内容。现在想起来,我当时在搞异步Action时,遭遇异常而不得不手动保持HttpContext就是这个原因造成的。于是我们目前修改的方式,便是为ViewPage和ViewUserControl增加一个额外的TextWriter参数:

  1. public class ViewPage : Page, IViewDataContainer {  
  2. ...  
  3. public virtual void RenderView(ViewContext viewContext, ViewContext writer) {  
  4. ViewContext = viewContext;  
  5. InitHelpers();  
  6. // Tracing requires Page IDs to be unique.  
  7. ID = Guid.NewGuid().ToString();  
  8. ProcessRequest(HttpContext.Current);  
  9. viewContext.HttpContext.Server.Execute(this, writer, false);  
  10. }  
  11. }  
  12.  
  13. public class ViewUserControl : UserControl, IViewDataContainer {  
  14. ...  
  15. public virtual void RenderView(ViewContext viewContext, ViewContext writer) {  
  16. viewContext.HttpContext.Response.Cache.SetExpires(DateTime.Now);  
  17. var containerPage = new ViewUserControlContainerPage(this);  
  18. // Tracing requires Page IDs to be unique.  
  19. ID = Guid.NewGuid().ToString();  
  20.  
  21. RenderViewAndRestoreContentType(containerPage, viewContext, writer);  
  22. }  

至于其他“顺其自然”的修改就不值一提了。

在我看来,这种问题可能不是ASP.NET MVC的设计问题(Design Issue),但是这也是它的内部实现的低级错误。对于此类问题,如果使用扩展的方式进行修改会显得沉重而麻烦,需要各种扩展和配置才能使用。之前项目中使用的便是基于“外部扩展”来回避“内部错误”的办法,而目前已经换成自行修改编译过的System.Web.Mvc.dll了。这个修改版本目前已经发布在CodePlex中的MvcPatch项目中,如果您感兴趣可以获取它的源代码并编译使用。

目前,MvcPatch包含两个修改,一个自然就是目前WebViewEngine的问题,而另一个便是之前提过的DefaultControllerFactory线程安全问题,以后我会补充更多设计方面的修改和扩展。在使用MvcPatch的时候,除了让您的项目引用正确的程序集之外,还必须将web.config文件中各类型的名称指向修改正确。因为使用ASP.NET MVC的模板创建项目时,它的web.config会使用GAC中注册的强类型的ASP.NET MVC 1.0程序集。如果修改不正确,在使用MvcPatch的程序集时便会遇到错误。

因此我们也可以发现,使用MvcPatch的好处在于,我们不需要使用外部扩展的方式来构建workaround,但是它也有缺点,那就是一些依赖于ASP.NET MVC 1.0程序集的项目无法和我们一起使用了。好在目前看起来这些项目都是些开源产品,如Telerik Extensions for ASP.NET MVC,我们可以下载它们的源代码,基于MvcPatch的程序集编译后再使用。

您别嫌麻烦,这就是享受开源的优势时需要付出的小小代价。

***再谈一件事情。昨天晚上写完文章之后,我想到这种“补丁版本”并不是长久之计,因此在CodePlex上给ASP.NET项目提了一个Issue:WebFormView总是输出至HttpContext.Current而不是指定的TextWriter。今天早上发现已经有了ASP.NET团队成员回复,他们表示内部的代码库中已经修改了这个问题,将会体现在ASP.NET MVC 2的Preview 2版本中。

原文标题:WebFormView的标准修改办法及MvcPatch项目

链接:http://www.cnblogs.com/JeffreyZhao/archive/2009/09/15/standard-webformview-patch-and-MvcPatch-project.html

【编辑推荐】

  1. ASP.NET MVC与WebForm区别谈
  2. 自定义的ControllerFactory:接口实现,支持Area
  3. ASP.NET Routing之“解析URL”功能详解
  4. 为ASP.NET MVC应用添加自定义路由
  5. 学习ASP.NET MVC路由的使用方法
责任编辑:彭凡 来源: 博客园
相关推荐

2009-06-15 10:40:14

2010-01-27 16:33:07

C++中标准输入

2011-03-29 09:14:49

Dispose模式C#

2009-09-14 17:08:02

WebFormView

2014-07-22 09:51:19

面板数据分析

2010-09-26 09:44:28

JVM启动参数

2015-10-16 09:33:26

TCPIP网络协议

2023-04-02 23:13:07

Go语言bufio

2011-06-24 15:16:33

Qt 插件

2010-01-12 11:37:34

VB.NET读取图像

2009-11-05 15:03:02

无线接入网

2009-11-27 09:41:56

LINQ

2009-12-25 10:31:31

Linux网络故障

2010-06-02 10:36:41

SVN开发

2009-11-30 10:55:16

PHP页面乱码

2009-11-10 10:24:12

无线网卡设备

2018-07-24 09:19:31

顶层架构区块链

2012-07-26 09:58:30

Windows 8触摸键盘

2023-09-27 08:26:48

Go标准库函数

2009-11-03 17:08:23

无线接入网
点赞
收藏

51CTO技术栈公众号