在ASP.NET 2.0中开发通配符映射应用程序的一些问题 (转自:http://www.cnblogs.com/dudu/archive/2006/03/07/345107.html)

转载 2007年09月14日 17:12:00

ASP.NET 2.0, 想说爱你不容易—在ASP.NET 2.0中开发通配符映射应用程序的一些问题

     文章的题目想了好几个,比如:“在ASP.NET 2.0中开发通配符映射应用程序的一些问题”,后来考虑到为了吸引眼球:),并为了好记,选了这个有点俗的题目。
     本文主要通过分析在ASP.NET 2.0中开发ASP.NET通配符映射应用程序遇到的一些问题,来说明ASP.NET 2.0中页面编译模型的不足之处。文章中如果有不妥之处,欢迎您指出。
     这里所说的ASP.NET通配符映射应用程序是指在IIS中将所有请求转发至ASP.NET 2.0运行时处理(对于IIS 5.0,就是建立.*到aspnet_isapi.dll的映射),在程序中通过实现System.Web.IhttpHandlerFactory接口来处理所有请求,实现System.Web.IhttpHandlerFactory的类就相当于一个前端控制器。典型应用就是.Text及基于.Text开发的博客园Blog软件。
     在ASP.NET 1.1中,实现通配符映射应用程序大家可能比较清楚,主要是两点:
     1、 实现System.Web.IhttpHandlerFactory接口,在GetHandler(HttpContext context, string requestType, string url, string path)中根据请求的url,基于一些规则,找到实际访问的页面文件,然后调用PageParser.GetCompiledPageInstance对页面进行编译并生成相应的实例处理请求。这样做的好处是:你可以使用任意的url地址,不必关心是否存在对应的页面文件,而且可以方便地控制对Web服务器上资源的访问。
     2、 在web.config中加上:

<httpHandlers>
    
<add path="*" verb="*" type="Dottext.Common.UrlManager.UrlReWriteHandlerFactory,Dottext.Common" validate="false" />
</httpHandlers>

     ASP.NET 2.0中新的页面编译模型给实现通配符映射应用程序带来意想不到的问题,下面我以博客园Blog软件为例与大家一些探讨这些问题。
     在博客园Blog软件中,实现IhttpHandlerFactory接口的是Dottext.Common.UrlManager. UrlReWriteHandlerFactory,不改变在ASP.NET 1.1中实现的UrlReWriteHandlerFactory代码,直接在ASP.NET 2.0中编译并运行,当程序运行在IIS根目录下,就会在执行PageParser.GetCompiledPageInstance时出现“Object reference not set to an instance of an object”异常(运行在虚拟目录中不会出现这个问题)。这个问题是ASP.NET 2.0中的一个小Bug,之前我写的PageParser.GetCompiledPageInstance中的一个Bug及解决方法对这个问题进行了一些分析,这个问题可以通过在PageParser.GetCompiledPageInstance之前调用context.RewritePath("~/default.aspx")解决。
      接着进行访问个人Blog主页的测试,比如:http://www.cnblogs.com/dudu,访问时出现错误:

“There is no build provider registered for the extension ''. You can register one in the <compilation><buildProviders> section in machine.config or web.config. Make sure is has a BuildProviderAppliesToAttribute attribute which includes the value 'Web' or 'All'.”

     在ASP.NET 2.0中,当我们第一次访问一个页面时,必不少的两个过程是:1、页面编译 2、创建编译后的页面代码的实例。页面编译是根据所访问的url地址中的扩展名找到匹配的Build Provider对页面进行编译。这里出现的问题是由于ASP.NET 2.0运行时没找到相应的Build Provider,对http://www.cnblogs.com/dudu这样地址,由于使用了通配符映射,在ASP.NET 2.0运行时处理时,得到的扩展名是空(如果没有使用通配符映射,IIS会自动将地址改为:http://www.cnblogs.com/dudu/default.aspx)。ASP.NET 2.0在这里的设计不足之处是没有考虑这种情况,无法通过在web.config中进行相应的配置来解决这个问题。如果能提供下面的配置,这个问题就可以轻松解决:

<buildProviders >
        
<add  extension=".*" type="System.Web.Compilation.PageBuildProvider"/>
</buildProviders>

     对于这个问题,我的解决方法是在PageParser.GetCompiledPageInstance之前对url进行处理,在url中加上缺省文件,比如:default.aspx。如果你想使用其他的扩展名,比如:.html,需要在web.config中加上:

<buildProviders >
        
<add  extension=".html" type="System.Web.Compilation.PageBuildProvider"/>
</buildProviders>

     这里还有一个小bug,在上面的错误信息“Make sure is has a BuildProvider AppliesToAttribute attribute which includes the value 'Web' or 'All'.”提示需要设置AppliesToAttribute属性,实际上web.config中并不支持这样的属性,可能是正式版之前的ASP.NET 2.0支持过这个属性,后来去掉后,错误提示信息并没有修改。
     解决了上面的两个问题,原以为通配符映射应用程序可以在ASP.NET 2.0中正常运行了,我在本机上测试博客园的程序,页面能正常访问。可是今天凌晨在服务器进上将网站升级到ASP.NET 2.0之后,发现ASP.NET运行时在频繁地编译页面,CPU占用一直100%,编译了一个多小时还在编译,而且编译似乎与访问量有关,访问少的站点页面还能打开,博客园主站由于访问量大,几乎无法访问。问题出在哪?于是我从PageParser.GetCompiledPageInstance的源代码寻找线索,在BuildManager.GetCacheKeyFromVirtualPath中发现可疑之处,BuildManager是根据所请求的虚拟路径创建缓存键,然后根据这个键查找或创建页面编译后的缓存对象。当对一个页面发出请求时,BuildManager会检查缓存,先从内存中检查,如果内存中没有就从缓存文件夹(Temporary ASP.NET Files)中查找,如果找到,就直接创建该类型的实例,不进行动态编译。如果没找到,就进行页面编译工作,而且查找的依据就是根据虚拟路径创建的缓存键。对于通常的页面访问方式,这样处理不会引起问题。但对于通配符映射的情况,就会带来问题。因为通配符映射时,常见情况是不同的url地址访问的却是同一个页面文件。比如博客园中每篇文章地址不同,但访问的却是同样的页面代码,如果按照目前ASP.NET 2.0中的页面编译模型,每篇文章第一次访问都要进行编译,如果博客园中的几十万篇文章被访问,就要进行几十万编译,难怪今天博客园网站升级至ASP.NET 2.0之后,服务器一直忙于编译。
经过测试情况果然这样,当然访问地址:http://www.cnblogs.com/dudu/archive/2006/03/07/345107.html时会在Temporary ASP.NET Files中文件夹编译生成类ASP.dudu_archive_2006_03_07_345107_html,而访问其他文章地时,也根据文章地址生成另外一个类(2006年3月12日修改:对于这个问题,通过传给PageParser.GetCompiledPageInstance一个真实的虚拟地址就能解决问题,比如在博客园程序中,对于上面的地址,改为这样的代码就行了:GetCompiledPageInstance(app+"~/default.aspx", pagepath, context))。  这样编译效率实在太低了!为什么要根据虚拟路径创建缓键,设计者设计时根本没考虑到通配符映射的问题,真是糟糕的设计!如果按照ASP.NET 1.1那样根据实际访问的页面文件名创建缓存键,就可以轻松地避免这个问题。ASP.NET 2.0新的页面编译模型在这里似乎是一个败笔。更糟糕的是连让开发人员弥补这个Bug的机会都没有,System.Web.Compilation.BuildManager中没有提供一个让开发人员自己设置缓存键的方法或属性。(注:创建缓存键的方法是BuildManager. GetCacheKeyFromVirtualPath(VirtualPath virtualPath, out bool keyFromVPP))。更糟糕的是,System.Web.Compilation中的很多类都是internal,很多类的方法是灰色(Reflector用灰色显示internal static或private,颜色用的不错,让人看了就灰心),想自己调用相应方法进行页面编译几乎是不可能(用反射的方法不知能否调用,还没试过,即使能调用,也要考虑性能上的损失)。难道要自己写System.Web.Compilation中那些类去处理页面编译?我宁愿选择ASP.NET 1.1,然后等ASP.NET 2.0 SP1,SP1解决不了,等SP2......希望不要等到ASP.NET 3.0。

    也许你想到了在GetHandler(HttpContext context, string requestType, string url, string path)中调用System.Web.Compilation.BuildManager.CreateInstanceFromVirtualPath来编译并创建页面的实例。这个方法我也尝试过,答案是不行,还不如PageParser.GetCompiledPageInstance,至少后者能让程序运行起来。使用BuildManager.CreateInstanceFromVirtualPath时,当访问的地址中不带扩展名时就会出现“The resource cannot be found”错误,原因是在GetVPathBuildResultInternal(VirtualPath virtualPath, bool noBuild, bool allowCrossApp, bool allowBuildInPrecompile)中调用了Util.CheckVirtualFileExists(virtualPath)对虚拟路径进行检查,检查时将虚拟路径转换为物理路径,检查当前请求的页面文件是否存在,对于通配符映射应用程序,很多地址是实际上不存在的,所以就出现“The resource cannot be found”错误。而PageParser.GetCompiledPageInstance中通过调用HostingEnvironment.AddVirtualPathToFileMapping避免了这个问题。而这个方法被
Internal保护,在代码中也无法调用。
      我觉得问题的核心是ASP.NET 2.0设计者在设计时没有考虑通配符映射这样的情况。是忽略还是另有考虑,就不得而知了。但ASP.NET 1.1能正确处理这个问题,而ASP.NET 2.0却处理不了,这里很不应该的。使用通配符映射的Web应用程序用户只能望ASP.NET 2.0心叹。最近花了很大精力想把博客园的程序迁移到ASP.NET 2.0,而结果却是无法迁移到ASP.NET 2.0,令人失望! 只能寄希望微软推出相应的补丁。
     还好,使用通配符映射的Web应用程序不是很多,这个问题影响不是很大。 
 

相关文章推荐

asp.net 返回上一页的实现方法小集(转)(http://www.cnblogs.com/liping19851014/archive/2007/07/14/818173.html)

返回上一页的这个东东在我们做项目的时候一般是用于填写完表单后确认的时候,有对原来输入的数据进行修改时用的,或者是因为网站为了方便浏览者而有心添加的一个东东,一般这种功能的实现在ASP.net中都是用一...

http://www.cnblogs.com/waxdoll/archive/2006/03/12/348464.html

要分析一个比较大的数据库,里面的表太多了,虽然是中文命名,但在2005的Management Studio中查看还是比较麻烦,比如,我想查看具有相同字段名称的表的情况就不好办。于是用SMO写了这个东东...

C++项目中的extern "C" {}(转自:http://www.cnblogs.com/skynet/archive/2010/07/10/1774964.html)

引言 在用C++的项目源码中,经常会不可避免的会看到下面的代码: #ifdef __cplusplus extern "C" { #endif /*...*/ #ifdef __c...

tar命令(转自http://www.cnblogs.com/qq78292959/archive/2011/07/06/2099427.html)

tar -c: 建立压缩档案 -x:解压 -t:查看内容 -r:向压缩归档文件末尾追加文件 -u:更新原压缩包中的文件 这五个是独立的命令,压缩解压都要用到其中一个,可以和别的命令连用但只...

转自http://www.cnblogs.com/jscode/archive/2012/07/10/2583856.html

常见前端面试题之HTML/CSS部分 转自http://www.cnblogs.com/jscode/archive/2012/07/10/2583856.html Doctype是什么?如何...

编辑器之神vim交流学习——http://www.cnblogs.com/azheng007/archive/2012/07/03/vim.html

1.vim 是什么 2.vim与emacs 3.为什么要学习vim 4.常用的gui编辑器与vim对比 体现vim的优势 5.vim常用命令介绍 6.配合开发相关命令介绍...

TCP/IP 协议介绍(转自)http://www.cnblogs.com/li0803/archive/2008/11/03/1324746.html

http://www.cnblogs.com/li0803/archive/2008/11/03/1324746.html Author :Jeffrey引言           ...

C#写文件常用的五种方法(转自:http://www.cnblogs.com/kiddo/archive/2008/03/17/1110061.html)

C#文件处理操作必须先导入命名空间: using System.IO; //实现背景:一个文本框、一个按钮、VS2005 方式一:用FileStream //实例化一个保存文件对话...

从一个程序的Bug解析C语言的类型转换(转自:http://www.cnblogs.com/dolphin0520/archive/2011/10/15/2212834.html)

先看下面一段程序,这段程序摘自《C 专家编程》: #include int array[]={23,34,12,17,204,99,16}; #define TOTAL_ELEMENTS (si...

C# 启动外部程序的几种方法(转自:http://www.cnblogs.com/Slxj/archive/2012/01/12/2320531.html)

1. 启动外部程序,不等待其退出。  2. 启动外部程序,等待其退出。  3. 启动外部程序,无限等待其退出。  4. 启动外部程序,通过事件监视其退出。 // using Syste...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)