为非回调页面保存状态
[本文为摘抄,原作者:风谷]
做ASP.NET开发的人都知道,控件使用ViewState保存自身的状态,并通过回调时的表单值来控制自身的行为,例如显示数据、触发事件等。在默认情况下,ViewState是通过发送到浏览器页面中的一个隐藏域(HiddenField)来进行值的传递,因此,只有在页面回调的时候才能取到ViewState中的相应值。当然,这也是ASP.NET的基本处理方式。
但在某些应用上,这种处理方式可能会无法满足我们的需求。举例来说,一个典型查询页,上面会提供一些查询条件让用户选择,用户在输入查询条件之后找到自己想要的结果,然后点击相应的结果进入另一个页面(姑且称之为内容页吧),在这个页面中完成自己操作后,返回到查询页。一般情况下,我们所提供的返回功能往往是只一个到查询页的链接,那么进入重新进入这个页面时,之前所输入的查询条件都将会丢失,自然查询结果也就不会存在了。这时,如果用户需要先前的查询条件和结果,甚至是页码,就必须重复先前的查询操作去获取结果。
上述这种情况对于一些简单的查询来说并不是很大的问题,但对于一些数据量大、查询条件复杂的情况会非常麻烦,用户不得不多次重复做一些相同的事,给使用带来了级大的不便。要如何优化这种情况呢?一些人可能会说,返回功能不要用链接,而是用脚本在客户端调用history.back()让浏览器后退一步就可以了。不错,这是一种解决办法,但是,如果在内容页里进行了一些回调,这种简单的“后退”仍旧是不合适的。那么,要怎么样做才能满足要求呢?
先来分析一下问题,控件的状态是通过ViewState保存的,因此要还原先前用户输入的内容了,就必须在页面处理请求时加载ViewState,另外,为了完整地还原页面的状态,除了ViewState以外,还需要保存从客户端POST过来的所有表单内容。但如本文开头所说,ViewState是保存中浏览器的隐藏域里,如果不是回调就没办法取到,另外,ASP.NET也不会在非回调的情况下加载ViewState。很明显,如果要在非回调的情况下还原控件状态,有两个问题需要解决,一是要在非回调的情况下获取最后一次请求页面时ViewState和表单的内容,二是要让Page认为当前请求为回调,即Page.IsPostBack属性值为true,因为只有这样才能完整地模拟PostBack操作。
要解决第一个问题并不复杂,我们可以把ViewState和表单的内容保存在服务端内存中,可以重载Page.SavePageStateToPersistenceMedium方法将值存入服务端内存。但重载该方法并不能直接获取表单内容,但可以通过Page.DeterminePostBackMode方法取得。这样,只要把两个变量以合适的方法放入内存就可以了。我选择的办法是用一个Pair对象保存视图和表单内容,再将它们放入Session中。当然,除了Session,还可以用HttpRuntime.Cache等。如果要加载保存在服务端内存中的内容,可以重载Page.LoadPageStateToPersistenceMedium方法。
第二个问题就是要让Page认为当前是回调模式,即IsPostBack返回的值应为true。通过对System.Web源代码的分析,我发现当Page.DeterminePostBackMode方法返回的值不是空值时,就可以达到我们的目的,而这个方法又是可以重载的,这样问题就变得简单了。重载DeterminePostBackMode方法,若基类方法返回的值是空值,就从先前在Session中保存的状态取得上一次保存的表单值。其实关于DeterminePostBackMode方法,MSDN中的解释并不多,我也是从字面上猜测,结果对源码的分析得来的,是否合理还有待验证。
OK,两个主要问题都解决了,接下来就可以用代码来实现。
Pair _state;
/// <summary>
/// 已重载。如果可能,从服务端内存中还原视图内容。
/// </summary>
/// <returns></returns>
protected override object LoadPageStateFromPersistenceMedium()
{
object state;
if (_state != null)
state = _state.First;
else
state = base.LoadPageStateFromPersistenceMedium();
return state;
}
/// <summary>
/// 已重载。将当前视图内容和请求参数集合保存至服务器内存。
/// </summary>
/// <param name="state"></param>
protected override void SavePageStateToPersistenceMedium(object state)
{
base.SavePageStateToPersistenceMedium(state);
var ps = _state;
if (ps == null)
{
ps = new Pair(state, base.DeterminePostBackMode());
Session["page_state"] = ps;
}
else
ps.First = state;
}
/// <summary>
/// 已重载。当基类方法返回空值时,尝试从服务端内存中获取先前保存的请求参数集合。
/// </summary>
/// <returns></returns>
protected override System.Collections.Specialized.NameValueCollection DeterminePostBackMode()
{
var coll = base.DeterminePostBackMode();
if (coll == null)
{
_state = Session["page_state"] as Pair;
if (_state != null)
coll = _state.Second as System.Collections.Specialized.NameValueCollection;
}
return coll;
}
以上代码的实现方法仅提供一种思路,并不是最终解决办法,因为还有许多细节的问题要考虑,并且也没有进行过广泛的应用,因此对可能造成的问题基本上是不可知的。欢迎有兴趣的人一起讨论。
■ 拿起勇气,探索你未知的世界。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 周边上新:园子的第一款马克杯温暖上架
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?
· 使用C#创建一个MCP客户端