细节决定成败 ASP.NET中的蝴蝶效应

2024-04-17 01:18

本文主要是介绍细节决定成败 ASP.NET中的蝴蝶效应,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

来源:中国IT实验室
  前言

  ASP.NET的优点也就是各个控件独立负责自己内部的逻辑,这是一个好事情,因为它解决了原本ASP处理逻辑耦合度高的问题。然而这是需要代价的,那就是引入ASP.NET页面生命周期,随着控件的多层嵌套,应用的复杂度增加,我们再次陷入泥潭!

  问题

  其实这个文章题目我两个月前就写下了,可是一直没想写完它,直到今天我在这个泥潭中泡了几个小时,于是决定先从泥潭中跳出来把文章写完,再跳进去继续解决问题。问题是这样的:

  使用MS AJAX 1.0 Beta2 + 2.0 CTP新建一个项目,同时在Bin中放上Beta2的AjaxControlToolkit.dll.
  扔上一个Accordion,放置几个AccordionPane,设置一下CssClass.
  在Page_Load中使用Page.LoadControl加载一个UserControl,然后添加到页面上。

  接着发现UserControl内的控件无法正常触发事件,陷入泥潭中……
  首先要说明,如果仅仅做第3步那个UserControl肯定正常运作,那意味着问题出在ScriptManager或Accordion中出现了问题。
  正文

  想知道到底是什么出问题了吗?先听我说说这个ASP.NET页面生命周期的问题吧。
  由于生命周期按阶段划分,任务在不同阶段按部就班完成,所以我们的每一个操作都是阶段相关的,有些操作仅能在特定的阶段操作,有些操作在不同阶段执行会导致不同的结果。当然,MS希望尽量消除这些阶段间的差异,例如让一个操作在尽可能多的阶段中都能执行,并且尽可能减少在不同阶段中操作引发的不同结果。然而这不可能完全做到,例如我们都知道ViewState读写限制为仅能在某些阶段进行,于是依赖于ViewState的控件属性也就因此受到同样的限制。

  控件属性读写受阶段限制,这很好接受,对吧?因为这仅仅是一层依赖关系。顺着依赖关系推广出去,情况会变得越来越复杂,限制的原因埋藏得越来越底层,接着我们发现复杂性这一问题在ASP.NET这种结构良好的体系中出现了,而消灭这种复杂性的银弹还没被发明。

  作为控件或组件的开发人员,我们当然有义务消除阶段差异,让下游的开发人员面对更低的复杂性,而且我们也确实尽力去做了。控件的每一层封装,都包含着这种努力,并向上承诺尽可能低的阶段差异。然而为了让控件看起来简单易用,我们不可能将这些差异完整地记录在文档之中,我们尝试去隐瞒细节,控件被层层封装时我们都这样做。底层文档没告诉我的差异,我当然也没必要写到这一层的文档上去;底层文档提及了的差异,我尽力弥补了,即使弥补得不太好,也不写到这一层的文档上去。于是文档就好像神话传说一样随着世代相传而改变,最终没有人知道这个控件依赖于某些底层的阶段差异。

  做过控件开发的人都知道,有时候我们必须根据实际情况采用不同的方式构建看起来一样的控件。例如最简单的数据控件都会存在是否PostBack的构建差异,如果是非PostBack,则需要在DataBind时构建并将数据保存到ViewState,如果是PostBack则根据ViewState直接构建,如果PostBack后又遇到了DataBind则需要清除原来的构建并重新根据新数据构建。再复杂一些的控件,还会分步骤构建,默认情况下为了消除使用方的阶段差异,部分构建步骤会尽可能靠前到Init时执行,而另外一部分构建步骤则尽可能推迟到PreRender时执行,中间部分则尽可能减少自己的变化以便使用方操作。然而事情不会那么简单,使用方的某些操作(通常是访问某个属性)如果依赖于某个构建步骤的完成,因此一旦这些操作出现,原本在PreRender才执行的特定构建步骤就要提前执行,当这样的操作在不同阶段进行多几次,构建步骤就已经散落在页面生命周期的各阶段。

  构建步骤可能散落于页面生命周期的各阶段对于控件设计师来说是一个严峻的问题,这意味着他要保证任何一个构建步骤在任何一个阶段执行都是无差异的,当然这不可能做到,于是又要引入别的机制来减少这种差异,复杂性就此产生了,接下来随着复杂性的增加控件设计师越来越无法确保较低的阶段差异程度,这就到控件使用者遭殃了,如果控件使用者又再把控件封装,并且依然企图降低阶段差异程度,那么灾难也就发生了……

  结果

  我花了几个小时在泥潭中泡了几个小时,边泡边写这篇文章,问题当然已经有结果了。

  如果Accordion设置了HeaderCssClass或者ContentCssClass,那就会出问题,但如果为AccordionPane都加上以上两个属性,又不会有问题了。这样的情况当然通过用Reflector查看这两个类的代码来解决,结果发现Accordion会检测每一个AccordionPane是否有设置这两个属性,如果没有就把AccordionPane的设置为和自己的一样。在AccordionPane被设置时,会调用this.EnsureChildControls(),这是一个会导致构建步骤提前执行的方法,于是控件构建的顺序就改变了,不仅仅Accordion内部的顺序改变了,整个Page的都改变了。由于控件的ID是按顺序自动分配的,包括我那个UserControl,构建顺序的改变意味着ID的改变,也就相当于整个控件树都改变了,事件当然不能正常触发。

  最后的解决方案当然是为我那个UserControl指定ID.我花了那么多个小时才发现自己做了件蠢事,一早打开Trace来看控件树就应该能发觉UniqueID的变化。

  总结

  虽然这个问题看起来不是一个太好的例子,因为一打开Trace就应该能找到问题的来源,但实际上它却正好揭示了ASP.NET框架内部的“蝴蝶效应(Butterfly Effect)”——随着复杂度的增加,任何一个细微的改变都会导致全局上的巨大变化。在设计ASP.NET的时候,MS可能也在想着解耦,在简单的情况下这东西确实也解耦,然而在复杂的情况下却正好背道而驰,这真的是很讽刺。 

这篇关于细节决定成败 ASP.NET中的蝴蝶效应的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/910417

相关文章

C#借助Spire.XLS for .NET实现在Excel中添加文档属性

《C#借助Spire.XLSfor.NET实现在Excel中添加文档属性》在日常的数据处理和项目管理中,Excel文档扮演着举足轻重的角色,本文将深入探讨如何在C#中借助强大的第三方库Spire.... 目录为什么需要程序化添加Excel文档属性使用Spire.XLS for .NET库实现文档属性管理Sp

C#借助Spire.XLS for .NET实现Excel工作表自动化样式设置

《C#借助Spire.XLSfor.NET实现Excel工作表自动化样式设置》作为C#开发者,我们经常需要处理Excel文件,本文将深入探讨如何利用C#代码,借助强大的Spire.XLSfor.N... 目录为什么需要自动化工作表样式使用 Spire.XLS for .NET 实现工作表整体样式设置样式配置

C#利用Free Spire.XLS for .NET复制Excel工作表

《C#利用FreeSpire.XLSfor.NET复制Excel工作表》在日常的.NET开发中,我们经常需要操作Excel文件,本文将详细介绍C#如何使用FreeSpire.XLSfor.NET... 目录1. 环境准备2. 核心功能3. android示例代码3.1 在同一工作簿内复制工作表3.2 在不同

在.NET项目中嵌入Python代码的实践指南

《在.NET项目中嵌入Python代码的实践指南》在现代开发中,.NET与Python的协作需求日益增长,从机器学习模型集成到科学计算,从脚本自动化到数据分析,然而,传统的解决方案(如HTTPAPI或... 目录一、CSnakes vs python.NET:为何选择 CSnakes?二、环境准备:从 Py

C#使用Spire.Doc for .NET实现HTML转Word的高效方案

《C#使用Spire.Docfor.NET实现HTML转Word的高效方案》在Web开发中,HTML内容的生成与处理是高频需求,然而,当用户需要将HTML页面或动态生成的HTML字符串转换为Wor... 目录引言一、html转Word的典型场景与挑战二、用 Spire.Doc 实现 HTML 转 Word1

Go语言使用net/http构建一个RESTful API的示例代码

《Go语言使用net/http构建一个RESTfulAPI的示例代码》Go的标准库net/http提供了构建Web服务所需的强大功能,虽然众多第三方框架(如Gin、Echo)已经封装了很多功能,但... 目录引言一、什么是 RESTful API?二、实战目标:用户信息管理 API三、代码实现1. 用户数据

在ASP.NET项目中如何使用C#生成二维码

《在ASP.NET项目中如何使用C#生成二维码》二维码(QRCode)已广泛应用于网址分享,支付链接等场景,本文将以ASP.NET为示例,演示如何实现输入文本/URL,生成二维码,在线显示与下载的完整... 目录创建前端页面(Index.cshtml)后端二维码生成逻辑(Index.cshtml.cs)总结

解决hive启动时java.net.ConnectException:拒绝连接的问题

《解决hive启动时java.net.ConnectException:拒绝连接的问题》Hadoop集群连接被拒,需检查集群是否启动、关闭防火墙/SELinux、确认安全模式退出,若问题仍存,查看日志... 目录错误发生原因解决方式1.关闭防火墙2.关闭selinux3.启动集群4.检查集群是否正常启动5.

解决未解析的依赖项:‘net.sf.json-lib:json-lib:jar:2.4‘问题

《解决未解析的依赖项:‘net.sf.json-lib:json-lib:jar:2.4‘问题》:本文主要介绍解决未解析的依赖项:‘net.sf.json-lib:json-lib:jar:2.4... 目录未解析的依赖项:‘net.sf.json-lib:json-lib:jar:2.4‘打开pom.XM

javax.net.ssl.SSLHandshakeException:异常原因及解决方案

《javax.net.ssl.SSLHandshakeException:异常原因及解决方案》javax.net.ssl.SSLHandshakeException是一个SSL握手异常,通常在建立SS... 目录报错原因在程序中绕过服务器的安全验证注意点最后多说一句报错原因一般出现这种问题是因为目标服务器