那個貼子太長,我雖然有回復,不過應該沒有幾個人注意到我的不同觀點,所以開一貼討論。歡迎sp1234及其他有興趣的朋友進來討論。如果回復較多,再加分。
原貼
http://topic.csdn.net/u/20080530/22/ef0d36a4-6bd4-4eba-9743-b56f080b2161.html
下面是sp1234的觀點及代碼:
如果你有一个非常酷的页面,页面上很多东西自动地响应用户操作而展现丰富的变化,你的ViewState是很有可能达到200K的。
这里是我将ViewState持久化保持在服务器端的代码,这样ViewState不占用网络带宽,因此其存取只是服务器的磁盘读取时间。并且它很小,可以说是磁盘随便转一圈就能同时读取好多ViewState,因此可以说“不占时间”。为了再“不占磁盘时间”,我还使用了缓存。
不使用Session,因为它会“丢失”。ViewState保存在磁盘上,即使服务器重新启动,也不会丢失页面状态。
这可以确保绝对稳定可靠地工作。以后请放心使用ViewState,把交互式页面提高水平才是最重要的,不要纠缠在“ViewState太大”上。实际上,由于页面设计不够酷,交互变化看上去不够丰富,ViewState实在是太小太小了。
如果你使用了它有效提高了复杂交互页面的效率,可以说一下提高了多少?!如果你觉得没用,也可以说一下在什么情况下没用。
protected override object LoadPageStateFromPersistenceMedium()
{
var viewStateID = (string)((Pair)base.LoadPageStateFromPersistenceMedium()).Second;
var stateObject = Cache[viewStateID];
if (stateObject != null)
return stateObject;
var fn = this.Server.MapPath("~/App_Data/ViewState/" + viewStateID);
var stateStr = File.ReadAllText(fn);
return new ObjectStateFormatter().Deserialize(stateStr);
}
protected override void SavePageStateToPersistenceMedium(object state)
{
var viewStateID = (DateTime.Now.Ticks + this.GetHashCode()).ToString(); //产生离散的id号码
Cache.Insert(viewStateID, state);
ThreadPool.QueueUserWorkItem(obj =>
{
var value = new ObjectStateFormatter().Serialize(state);
var fn = this.Server.MapPath("~/App_Data/ViewState/" + viewStateID);
File.WriteAllText(fn, value);
});
base.SavePageStateToPersistenceMedium(viewStateID);
}
下面是我的觀點:
这么多人跟贴,我也来发表下意见,探讨一下。
总体来说,LZ的思路对于某些场景是非常有用的,但是绝对不宜大量推广,一些还不能够分清利弊的程序员更不应该像楼主所说的“以后请放心使用ViewState,把交互式页面提高水平才是最重要的,不要纠缠在“ViewState太大”上”。
因为这个方法虽然节省了客户端与服务器间的带宽传输,但是加大了服务器的负荷(包括服务器的CPU负荷、其他性能消耗以及磁盘空间,特别是访问量大的站点绝不应该这样使用。另外,LZ实现的方法值得商榷,破坏了ASP.NET已经设计好的架构(视图保存及读取部分).
细述如下(针对楼主116楼的最终写法):
1. 每次页面运行都至少要写缓存一次,读缓存了次,写磁盘一次,来保存或读取视图。
首先,每次都要存缓存,尽管LZ是为了获得从缓存中读取视图的性能提升,但这样操作缓存是否太频繁了?虽然读的时候提升了速度,但是存的时候却损失了保存缓存所消耗的CPU以及缓存空间。
其次,每次使用磁盘来保存视图,也是个问题。碰盘的操作有瓶颈,这是无法回避的问题,使用线程,即使是使用.NET内置的线程池,但终究还是增加了系统及CPU的开销,关于线程,在第2点中还会讲到。
对于一个访问量不大的站点,上述的这些问题不会很明显,但是一旦访问量大的话,服务器的性能就会有影响了,并发响应能力也会降低。
2. 关于线程
线程性能问题,上面已经提到,就不再说了。
由于楼主使用的是.NET内置的线程池,线程池的大小是有限制的,请设想这样一种场景:当客户端请求服务器上的一个页面,保存视图时,在排队等待线程池时却没有可用的线程。而此时客户端已经回发过来,需要加载视图状态,恰巧由于缓存机制把缓存的视图清空,而这时上一次的视图却还没有保存到磁盘!
这时,这个视图机制就等同于失效了。尽管一般情况下,发生这种情况的可能性不大,但是当并发访问量大时,绝不能排除它的发生。
3. 关于SavePageStateToPersistenceMedium和LoadPageStateFromPersistenceMedium的实现,也就是我在之前所说的“破坏了ASP.NET已经设计好的架构”
直接复制MSDN上的资料:
LoadPageStateFromPersistenceMedium 方法使用 PageStatePersister 属性所引用的 System.Web.UI.PageStatePersister 对象的 Load 方法来为 Page 对象加载任何保存的视图状态信息。
ASP.NET 包括 PageStatePersister 类的两个子类:在 ASP.NET 页包含的隐藏字段中保存状态信息的 HiddenFieldPageStatePersister 类和在与请求关联的 Session 对象中保存状态的 SessionPageStatePersister 类。
若要在您选择的位置保存状态,应创建 PageStatePersister 类的新子类,它可将状态保存并加载到您所选择的持久性介质中。有关创建新的 PageStatePersister 对象的示例,请参见 PageStatePersister 类。
使用 .NET Framework 1.0 版或 1.1 版时,如果要从隐藏字段外的其他任何位置加载 Page 状态,则请重写此方法。如果选择这样做,还必须重写 SavePageStateToPersistenceMedium 方法。
SavePageStateToPersistenceMedium 方法使用 PageStatePersister 属性所引用的 System.Web.UI.PageStatePersister 对象的 Save 方法存储页的视图状态和控件状态信息。
ASP.NET 包括 PageStatePersister 类的两个子类:在 ASP.NET 页包含的隐藏字段中保存状态信息的 HiddenFieldPageStatePersister 类和在与请求关联的 Session 对象中保存状态的 SessionPageStatePersister 类。注意,使用 SessionPageStatePersister 类时,隐藏的 VIEWSTATE 字段仍将像用于确定回发时那样呈现。
若要在您选择的位置保存状态,应创建 PageStatePersister 类的新子类,它可将状态保存并加载到您所选择的持久性介质中。有关创建新的 PageStatePersister 对象的示例,请参见 PageStatePersister 类。
使用 .NET Framework 1.0 版或 1.1 版时,如果要从隐藏字段外的其他任何位置保存 Page 状态,则请重写此方法。如果选择这样做,还必须重写 LoadPageStateFromPersistenceMedium 方法。