浅析WebFormView中的一个Bug

开发 开发工具
这里将介绍一个WebFormView中的Bug,它违反了ASP.NET MVC自身的设计,希望本文能对大家有所帮助。

老赵的文章主要是针对WebFormView中的一个Bug,这个Bug在一般情况下不会出现问题,它没有遵循ASP.NET MVC既定的模型。

最近需要搞一些重要的功能,结果又遇到了意料外的障碍。于是又仔细地看了看ASP.NET和ASP.NET MVC的源代码,又发现了以前不曾知道的一些细节。您最多说ASP.NET WebForms模型不一定适合某些Web应用程序的开发,但是我想没有人可以否认ASP.NET中设计的巧妙——以及复杂程度。其实ASP.NET为我们留下了不少切入点,但几乎没什么书会提到这些切入点,我们只能从微软自己的框架中一探究竟。

不过这次我想谈的是ASP.NET MVC框架中的一个Bug,这个Bug在一般情况下不会出现问题,但是这的确违反了ASP.NET MVC自身的设计。这个问题就出在WebFormView对象的实现上。

WebFormView是一个视图对象的实现。而在ASP.NET MVC中,任何视图都需要实现一个IView接口:

  1. public interface IView {  
  2.     void Render(ViewContext viewContext, TextWriter writer);  

Render方法的目的自然是根据ViewContext对象中的数据,将视图内容输出至TextWriter中。例如在HtmlHelper的RenderPartial方法,便是将一个Partial View输出至Response中.

  1. public class HtmlHelper {  
  2.     ...  
  3.     internal virtual void RenderPartialInternal(  
  4.         string partialViewName,  
  5.         ViewDataDictionary viewData,  
  6.         object model,  
  7.         ViewEngineCollection viewEngineCollection) {  
  8.  
  9.         ...  
  10.  
  11.         ViewContext newViewContext = new ViewContext(...);  
  12.         IView view = FindPartialView(newViewContext, partialViewName, viewEngineCollection);  
  13.         view.Render(newViewContext, ViewContext.HttpContext.Response.Output);  
  14.     }  

虽然我认为这里的做法是不太妥当的(这点下次再谈),但是这的的确确地表现了Render方法的设计意图。只可惜在WebFormView中,Render方法却违背了这一设计:

  1. public class WebFormView : IView {  
  2.     ...  
  3.     public virtual void Render(ViewContext viewContext, TextWriter writer) {  
  4.         ...  
  5.         object viewInstance = ...;  
  6.         ...  
  7.  
  8.         ViewUserControl viewUserControl = viewInstance as ViewUserControl;  
  9.         if (viewUserControl != null) {  
  10.             RenderViewUserControl(viewContext, viewUserControl);  
  11.             return;  
  12.         }  
  13.  
  14.         ...  
  15.     }  
  16.  
  17.     private void RenderViewUserControl(ViewContext context, ViewUserControl control) {  
  18.         ...  
  19.  
  20.         control.ViewData = context.ViewData;  
  21.         control.RenderView(context);  
  22.     }  

对于Partial View,WebFormView会加载合适的ViewUserControl实例,并调用其RenderView方法生成内容……但是,我们的writer参数到哪里去了?没错,对writer参数Find All Reference就会发现,这个参数根本没有用到。既然在这里就已经抛弃了我们指定writer,那么接下来的逻辑再怎么搞也就“那么一回事儿”了。

如果您感兴趣阅读代码的话,会发现事实上最终这个对象被放入了一个新建的ViewPage对象中,然后调用ViewPage的RenderView方法生成视图内容:

  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.     }  

瞧到这个HttpContext.Current了吗?也就是说,无论RenderView方法何时调用,永远是向HttpContext.Current输出内容。这个设计很不合理,但是修改起来还是非常简单的,例如以下几行代码就可以得到差不多的效果:

  1. public static class HtmlExtensions  
  2. {  
  3.     public static void 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.         htmlHelper.ViewContext.HttpContext.Server.Execute(  
  15.             page,  
  16.             htmlHelper.ViewContext.HttpContext.Response.Output,  
  17.             false);  
  18.     }  

但是我不喜欢这种做法,因为它没有遵循ASP.NET MVC既定的模型。ASP.NET MVC的确可以扩展,但如果需要按照标准扩展的话,我们作的事情就多了:

继承WebFormView,覆盖RenderView方法。

继承WebFormViewEngine,覆盖CreatePartialView方法,返回刚创建的新类。

在Application Start时,使用新的ViewEngine类替换ASP.NET MVC原有的视图引擎。

但是在实际情况中,我会选择使用使用第三种方法:下载ASP.NET MVC的源代码,改写,编译。既然它是MS-PL的授权协议,为什么不自己动手打一些Patch呢?事实上,我也打算使用这种方法来修补ASP.NET MVC的Bug或Design Issue,并发布一个临时的新项目,就叫作……MvcPatch如何?

原文标题:应该算是WebFormView的一个Bug

链接:http://www.cnblogs.com/JeffreyZhao/archive/2009/09/14/webviewengine-bug-always-render-to-current-context.html

【编辑推荐】

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

2009-07-14 16:02:42

JDBC例子

2013-06-18 11:37:42

XFSRHEL 6.4

2021-10-08 07:50:57

软件设计程序

2017-10-10 15:14:23

BUGiOS 11苹果

2024-04-22 00:00:01

Redis集群

2014-12-17 09:40:22

dockerLinuxPaaS

2022-05-16 08:42:26

Pandasbug

2023-03-13 08:09:03

Protobuffeature分割

2022-06-15 08:14:40

Go线程递归

2015-08-24 10:07:13

程序员bug

2019-08-01 12:59:21

Bug代码程序

2024-08-08 08:09:38

2011-05-19 15:14:49

PostgreSQL

2021-09-11 19:00:54

Intro元素MemoryCache

2011-03-03 21:04:08

bug程序员

2022-04-06 08:47:03

Dubbo服务协议

2018-11-01 13:23:02

网关APIHTTP

2009-08-31 13:53:03

C#创建一个文件

2017-03-22 09:11:45

bugbug赏金计划众包项目

2022-05-27 07:03:04

JDK场景线程
点赞
收藏

51CTO技术栈公众号