您的位置:首页 > 编程语言 > ASP

ASP.NET: 拦截并分发运行时动态创建控件的事件

2005-01-25 17:35 645 查看

ASP.NET: 拦截并分发运行时动态创建控件的事件

Sunday, December 07, 2003 3:06 AM

一、问题

在我目前参与设计的基于B/S体系的.NET分布式解决方案中,需要对Web界面服务层进行一定的性能优化以满足项目操作需求中关于保障终端浏览器在使用低速网络(如33.6/56kbps的调制解调器)时的响应速度的要求。使用ASP.NET开发的朋友们应该知道,我们在使用ASP.NET提供的众多RAD特性的时候,也是付出了一定的带宽和性能代价的,比如比较典型的例子就是对ViewState的不恰当使用。因此对界面服务层的很多性能优化都是在ViewState上做文章。

举个例子,在我们的方案中有大量的Web Form界面组件,其中大量应用了DataGrid等数据列表显示控件。这些组件大量使用了ViewState来保存列表本身的数据。如果你禁用了它的ViewState以减少页面传输数据量,则为了保持这些组件的状态(尤其是其中动态创建的子控件的状态)必须在页面Postback的时候重新绑定数据(当然,这时候可以有选择的使用服务器端的缓冲技术减少对后台数据库的压力)。

根据我的经验,以DataGrid为例,一般有这么几种使用情况:

1、显示一组数据,并允许用户进行添加、删除、修改等操作。一般而言,用于添加新数据的命令控件是独立于列表控件且静态声明的。然而删除、修改等命令控件基本都是在数据控件绑定数据时动态创建的。

2、若用户选择创建新数据,我们的用例是要求把用户导向用于创建新数据项的单独页面。也就是说,数据显示页面在执行此操作的时候不依赖于数据控件当前的状态。这种情况下,在Postback时是不需要数据控件原有的ViewState,且不需要对其重新进行数据绑定(即将转向其它页面了)。

3、若用户选择删除一条数据,我们的用例是要求刷新数据列表以反映操作结果。既然数据控件中的数据将被更新,则实际上不需要其原有的ViewState(但实际不然,见后文分析),因为在执行所请求的操作后需要对其重新进行数据绑定。

4、若用户选择更新一条数据,我们的用例是要求将用户转到用于修改所选数据项的页面。这种情况下,数据控件原有的ViewState是不需要的,因为马上就要转向其它页面。待修改完成再回到列表显示页面时,数据将被重新获取并绑定到数据控件上。

可以看出,在不需要就地数据编辑(比较适用于带宽较好的应用环境)的情形下,基本上是不需要在每次页面生成后保留数据控件的ViewState的。但这马上带来一个问题,如果Postback是由“数据绑定中动态创建的控件”引发的话,则在Postback回来Page_Load之后这个引发事件的控件必须仍然存在,而为了让这个控件在Postback回来的时候还存在,你只有两个选择:或者保留数据控件的ViewState让其自动恢复原来控件组合状态,或者你重新绑定数据,在这个过程中原来的控件还可以创建出来(*注意:如果再重新数据绑定的时候数据发生变化或被删除,则有可能事件被转发到错误的动态控件上去!因此最好手动为动态创建的控件设定ID使其可以对应到相关连的数据项上)。

以删除一条数据为例,如果用户按了某一行上的一个删除按钮,则该事件被Postback到后台页面处理的时候,ASP.NET页面处理引擎将解析到触发事件的按钮并将该事件转发给它的事件处理程序。在事件处理程序中我们其实只需要知道要删的是哪一行(或简单说,要删的那个数据的ID是什么)就足够了。而为了这一点信息,你或者要把整个数据控件的状态通过ViewState传来传去,或者在Page_Load的时候重新绑定数据——一切就为了一个可以定位操作目标的ID!

(待续……其间欢迎评论!要是有好的办法我就不再继续写了:)

二、思路

三、方案

四、后话

反馈

# 回复: ASP.NET: 拦截并分发运行时动态创建控件的事件

12/7/2003 4:14 AM by BigVan
关注ing

一直感觉。net的资源消耗太多了,有点不好。

虽然我做的项目对性能要求不高,但用户也抱怨新系统比老系统慢 。

# 回复: ASP.NET: 拦截并分发运行时动态创建控件的事件

1/17/2004 2:58 PM by sbhu
这篇文章很好懂的,讲的是怎样把 DataGrid 存放在 ViewState 中的无用数据(有时候确实如此)卡掉,大家知道,一般而言DataGrid在ViewState中会存放表格中的所有数据,这样会导致ViewState非常大;好比就这个论坛而言,光是ViewState就占了65k之多,多浪费啊。
  如果只是简单的将DataGrid的EnableViewState设置为false,那么许多事件,如排序、换页等都不会触发,那么岂不是得不偿失?其实在DataGrid中,有一个用来呈现数据的子控件:System.web.ui.webcontrol.DataGridTable,很遗憾,我在MSDN中没找到它的相关说明,但是我在跟踪DataGrid时,发现它的第一个子控件就是上述的Class(当然这是我在看到这片E文后才知道的,嘿嘿)。大家可以在SaveViewState中将生成的object的Type全部看看,再加上DataGrid.ViewState中的所有值,都不会发现DataGrid在ViewState中所保存的数据,那么是什么导致ViewState这么大,是什么在ViewState中存入数据源的数据呢?
  答案就在DataGrid的这个子控件(这个暗黑人物的存在还真让人的感情受不了)。所以呢,如果不需要在ViewState中存放这些数据源的数据(比如说为了加快速度,或者自定义分页),我们只需要:DataGrid.Controls[0].EnableViewState = false,就万事OK了。
  呵呵,没照原文翻,怕翻不好,不过大意是这样的。文中最后那几句话真实让我忍俊不已,我想很多人多是这样的,呵呵。斑竹,你认为这篇文章看的值不值呢?
===================================================
如果我是DataList或reapter是否也可以照样用?

DataList.Controls[0].EnableViewState = false

reapter.Controls[0].EnableViewState = false
==================================================
今天发现了一个超级好文章,和大家一起共享;板主页请来看看吧,很有帮助的
Thomas Skovsende
Hi there!

The scenario is this:
I have alot of datagrids with data in them. This makes my ViewState rather
large(upto 1MB), so I have tried to find ways to fix this.

I first tried storing my ViewState in a session instead by overriding
SavePageStateToPersistenceMedium/LoadPageStateToPersistenceMedium. This
works fine - except when people start to open more than one
window(viewstates starts to overwrite eachother), which will result in an
invalid cast.

Then i got the idea to make a new DataGrid, inherited from the original
DataGrid - and then override the LoadViewState/SaveViewState methods.(And
fetch the data from the db on every request)

But while doing this, i discovered that those methods only are responsible
for saving ViewState for themselves - NOT their children(The DataGridTable
in the DataGrid) - this is done by an undocumented method in Control which
is defined as:
internal object SaveViewStateRecursive()

The problem here is that it is NOT virtual - so i cant override it and stop
the framework from storing the ViewState of the actual data in the datagrid!

You might argue that i should disable the saving of viewstate and code
everything manually - well - tbh, i
a571
find it silly that i have to make alot
of extra code(and ny that introduce possible bugs), when all it would take
to solve my problem is to make SaveViewStateRecursive() virtual so i can
override it. If that happened, I would just override it for my custom
datagrid - viewstate wouldnt be saved for my grid, and no bugs would be
introduced(less likely anyway!)

I might be missing some obvious solution to my problem, but if not I would
love an argument for SaveViewStateRecursive() NOT to be virtual. I am aware
that there are a slight performance overhead, but I would say that this is
so little that it doesnt matter much!

Best regards,
Thomas Skovsende

Thomas Skovsende
*SIGH*

Sometimes i just hate being a developer - after battling with this problem
for a few good hours, the obvious solution appears:

protected override object SaveViewState()
{
Controls[0].EnableViewState = false; // Disable viewstate for the
DataGridTable - hardcoded - but works for now!
return base.SaveViewState();
}

Ahh well.. another case of "stupid developer"... :o)

Best regards,
Thomas Skovsende

# 回复: ASP.NET: 拦截并分发运行时动态创建控件的事件

1/17/2004 4:14 PM by JGTM'2004
@hbsu:

这个方法用来省却ViewState确实不错,谢谢你转来的资源!很遗憾最近忙得一直没空将此文完成,因为我的思路是朝着Command Dispatching的方向进行的(也就是完全不使用ViewState的前提下还能正确相应其中数据列上的命令请求)。不过对于大多数朋友来讲,文中的方法应该是够用了(不过需要派生一下DataGrid,可能还是比较麻烦吧)。

# re: ASP.NET: 拦截并分发运行时动态创建控件的事件

11/23/2004 5:01 PM by zpisgod
其实并不用派生datagrid,,在pageload中写上一句:
DataGrid1.Controls[0].EnableViewState = false就ok
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息