应该算是WebFormView的一个Bug
2016-07-29 00:00
288 查看
最近需要搞一些重要的功能,结果又遇到了意料外的障碍。于是又仔细地看了看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接口:
Render方法的目的自然是根据ViewContext对象中的数据,将视图内容输出至TextWriter中。例如在HtmlHelper的RenderPartial方法,便是将一个Partial View输出至Response中:
虽然我认为这里的做法是不太妥当的(这点下次再谈),但是这的的确确地表现了Render方法的设计意图。只可惜在WebFormView中,Render方法却违背了这一设计:
对于Partial View,WebFormView会加载合适的ViewUserControl实例,并调用其RenderView方法生成内容……但是,我们的writer参数到哪里去了?没错,对writer参数Find All Reference就会发现,这个参数根本没有用到。既然在这里就已经抛弃了我们指定writer,那么接下来的逻辑再怎么搞也就“那么一回事儿”了。
如果您感兴趣阅读代码的话,会发现事实上最终这个对象被放入了一个新建的ViewPage对象中,然后调用ViewPage的RenderView方法生成视图内容:
瞧到这个HttpContext.Current了吗?也就是说,无论RenderView方法何时调用,永远是向HttpContext.Current输出内容。这个设计很不合理,但是修改起来还是非常简单的,例如以下几行代码就可以得到差不多的效果:
但是我不喜欢这种做法,因为它没有遵循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如何?
不过这次我想谈的是ASP.NET MVC框架中的一个Bug,这个Bug在一般情况下不会出现问题,但是这的确违反了ASP.NET MVC自身的设计。这个问题就出在WebFormView对象的实现上。
WebFormView是一个视图对象的实现。而在ASP.NET MVC中,任何视图都需要实现一个IView接口:
public interface IView { void Render(ViewContext viewContext, TextWriter writer); }
Render方法的目的自然是根据ViewContext对象中的数据,将视图内容输出至TextWriter中。例如在HtmlHelper的RenderPartial方法,便是将一个Partial View输出至Response中:
public class HtmlHelper { ... internal virtual void RenderPartialInternal( string partialViewName, ViewDataDictionary viewData, object model, ViewEngineCollection viewEngineCollection) { ... ViewContext newViewContext = new ViewContext(...); IView view = FindPartialView(newViewContext, partialViewName, viewEngineCollection); view.Render(newViewContext, ViewContext.HttpContext.Response.Output); } }
虽然我认为这里的做法是不太妥当的(这点下次再谈),但是这的的确确地表现了Render方法的设计意图。只可惜在WebFormView中,Render方法却违背了这一设计:
public class WebFormView : IView { ... public virtual void Render(ViewContext viewContext, TextWriter writer) { ... object viewInstance = ...; ... ViewUserControl viewUserControl = viewInstance as ViewUserControl; if (viewUserControl != null) { RenderViewUserControl(viewContext, viewUserControl); return; } ... } private void RenderViewUserControl(ViewContext context, ViewUserControl control) { ... control.ViewData = context.ViewData; control.RenderView(context); } }
对于Partial View,WebFormView会加载合适的ViewUserControl实例,并调用其RenderView方法生成内容……但是,我们的writer参数到哪里去了?没错,对writer参数Find All Reference就会发现,这个参数根本没有用到。既然在这里就已经抛弃了我们指定writer,那么接下来的逻辑再怎么搞也就“那么一回事儿”了。
如果您感兴趣阅读代码的话,会发现事实上最终这个对象被放入了一个新建的ViewPage对象中,然后调用ViewPage的RenderView方法生成视图内容:
public class ViewPage : Page, IViewDataContainer { ... public virtual void RenderView(ViewContext viewContext) { ViewContext = viewContext; InitHelpers(); // Tracing requires Page IDs to be unique. ID = Guid.NewGuid().ToString(); ProcessRequest(HttpContext.Current); } }
瞧到这个HttpContext.Current了吗?也就是说,无论RenderView方法何时调用,永远是向HttpContext.Current输出内容。这个设计很不合理,但是修改起来还是非常简单的,例如以下几行代码就可以得到差不多的效果:
public static class HtmlExtensions { public static void Partial(this HtmlHelper htmlHelper, string partial) { var viewInstance = BuildManager.CreateInstanceFromVirtualPath(partial, typeof(object)); var control = viewInstance as ViewUserControl; control.ViewContext = htmlHelper.ViewContext; control.ViewData = htmlHelper.ViewData; Page page = new ViewPage(); page.Controls.Add(control); htmlHelper.ViewContext.HttpContext.Server.Execute( page, htmlHelper.ViewContext.HttpContext.Response.Output, false); } }
但是我不喜欢这种做法,因为它没有遵循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
- 某页游erlang服务端广播算法效率好差,应该算是一个bug了吧
- MySQL JDBC 5.1.25的一个坑(应该算是BUG)
- BUG平台应该是一个知识库
- 一个不应该发生的BUG。。。
- PL/SQL Developer View SQL功能的一个Bug
- 今天发现一个hibernate的bug,或者说一个应该注意的地方比较合适
- 艾伟也谈项目管理,BUG平台应该是一个知识库
- 这算是ASP.NET MVC的一个大BUG吗?
- Xcode 5 的一个 Bug:修改 TableView的 content导致崩溃
- Xcode 5 的一个 Bug:修改 TableView的 content 导致崩溃
- jsoncpp数组下标为0时的访问注意事项 这样应该算是严重bug
- .net1.1中 整除结果不一至的问题.这应该是.NET1.1的一个bug.
- 昨天在微信里看到的一篇文章,应该不算是个新闻,但是还是值得仔细读一下。特别是对于那些在一个公司干了很多年却没有发展,或者每年跳槽两三次的同学确实应该反思看一看。原文地址 以下是正文 我曾经带过一个
- 一个架构合理的UITableView应该是怎样的?
- 一个关于 UIPickerView 的 bug
- PL/SQL Developer View SQL功能的一个Bug
- 这个算是Visual Studio的一个BUG吗?
- 非等宽字体的编号对齐问题--这应该算是Office 2007的BUG吧?
- 烧饼排序问题(有待完善的地方,不过一个影响结果的bug被我找出来了,原来是第61行的比较运算符应该从<改成<=)