【整理】asp.net2.0状态管理新增特性
状态管理对于经常做asp.net的同行来说可以说是耳熟能详,比如客户端的ViewState、隐藏域、Cookie、查询字符串,服务器端的应用程序状态、会话状态等,这些相信大家都比较熟悉了,这里我主要说一下在asp.net2.0关于状态管理方面的新增功能,有视图状态分块、ControlState以及在web.config文件中属性保存会话状态,接下来我挨个介绍一下各自的用法。
首先,我说一下最简单的视图状态分块,这个东西提出的原因是当页面保存视图状态数据的隐藏域的内容比较大时,出于安全的原因,某些防火墙和代理会阻止对包含这些数据的页进行访问,为了绕过这个限制,才提出了这个概念。它允许我们将某个内容量相当大的存放视图状态数据的隐藏域,通过设置maxPageStateFieldLength来分解成大小固定、按顺序排列的若干个小的隐藏域。配置方式很简单,我们只需在web.config中如下设置即可(比较简单,我就不写演示了):
//设置每个子隐藏域的大小为50个字节
<pages maxPageStateFieldLength="50" />
<pages maxPageStateFieldLength="50" />
其次,我们说一下ControlState的用法,它允许您保持特定于某个控件的属性信息,且不能被关闭。提出的原因是为了解决当开发人员关闭页视图后保存控件的属性信息。若要使用控件状态,控件必须在初始化过程中调用RegisterRequiresControlState方法,然后重写SaveControlState和LoadControlState方法。
在页面文件中,我已将页试图状态和控件的视图状态同时关闭
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="StateDemo._Default" EnableViewState="false" %>
<%@ Register src="UCDemo.ascx" tagname="UCDemo" tagprefix="uc1" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<uc1:UCDemo ID="UCDemo1" runat="server" EnableViewState="false" />
<asp:Button ID="Button1" runat="server" Text="Button" onclick="Button1_Click" />
</div>
</form>
</body>
</html>
<%@ Register src="UCDemo.ascx" tagname="UCDemo" tagprefix="uc1" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<uc1:UCDemo ID="UCDemo1" runat="server" EnableViewState="false" />
<asp:Button ID="Button1" runat="server" Text="Button" onclick="Button1_Click" />
</div>
</form>
</body>
</html>
代码文件如下
protected void Button1_Click(object sender, EventArgs e)
{
UCDemo1.CurrentIndex++;
}
{
UCDemo1.CurrentIndex++;
}
上面代码中的用户自定义控件的cs文件如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace StateDemo
{
public partial class UCDemo : System.Web.UI.UserControl
{
private int currentIndex = 0;
protected override void OnInit(EventArgs e)
{
Page.RegisterRequiresControlState(this);
base.OnInit(e);
}
protected override object SaveControlState()
{
return currentIndex != 0 ? (Object)currentIndex : null;
}
protected override void LoadControlState(object savedState)
{
if (savedState != null)
{
currentIndex = (int)savedState;
}
}
public int CurrentIndex
{
set {
this.currentIndex = value;
}
get {
return this.currentIndex;
}
}
protected void Page_Load(object sender, EventArgs e)
{
Response.Write(this.currentIndex.ToString());
}
}
}
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace StateDemo
{
public partial class UCDemo : System.Web.UI.UserControl
{
private int currentIndex = 0;
protected override void OnInit(EventArgs e)
{
Page.RegisterRequiresControlState(this);
base.OnInit(e);
}
protected override object SaveControlState()
{
return currentIndex != 0 ? (Object)currentIndex : null;
}
protected override void LoadControlState(object savedState)
{
if (savedState != null)
{
currentIndex = (int)savedState;
}
}
public int CurrentIndex
{
set {
this.currentIndex = value;
}
get {
return this.currentIndex;
}
}
protected void Page_Load(object sender, EventArgs e)
{
Response.Write(this.currentIndex.ToString());
}
}
}
在上面代码中,如果我们不用ControlState(将OnInit、SaveControlState和LoadControlState三个方法注释掉),那么打印出来的始终都是0,因为状态不会保存;使用了ControlState后他的结果将随着按钮的点击依次递增。
最后,我们说一下如何永久性的保存会话状态。众所周知,我们经常用Session来保存会话状态,但是可惜的是Session会随着本次会话的结束而自动清空会话状态;如果想实现在会话结束时会话状态还存在,这就要用到我们的profile上场了,在这里我结合aspnetdb数据库来实现。在SQLServer2005中注册aspnetdb数据库请参见:http://www.cnblogs.com/cxy521/archive/2008/02/25/1080754.html,将aspnetdb数据库注册成功之后,接下来在web.config中添加profile节点(位于system.web节点下),这里比较简单的举个例子,如下
//若profile\properties下allowAnonymous="true",则必须启用anonymousIdentification
<anonymousIdentification enabled="true"/>
<profile>
<properties>
<add name="address" allowAnonymous="true" type="System.String"/>
<add name="zip" allowAnonymous="true" type="System.Int16"/>
</properties>
</profile>
<anonymousIdentification enabled="true"/>
<profile>
<properties>
<add name="address" allowAnonymous="true" type="System.String"/>
<add name="zip" allowAnonymous="true" type="System.Int16"/>
</properties>
</profile>
那么我是如何同数据库aspnetdb关联起来的呢,这个默认已经在machine.config中配置好,如下:
//链接到aspnetdb的连接串
<connectionStrings>
<add name="LocalSqlServer" connectionString="data source=.\SQLEXPRESS;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|aspnetdb.mdf;User Instance=true" providerName="System.Data.SqlClient"/>
</connectionStrings>
//配置profile与那个数据库关联
<profile>
<providers>
<add name="AspNetSqlProfileProvider" connectionStringName="LocalSqlServer" applicationName="/" type="System.Web.Profile.SqlProfileProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"/>
</providers>
</profile>
<connectionStrings>
<add name="LocalSqlServer" connectionString="data source=.\SQLEXPRESS;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|aspnetdb.mdf;User Instance=true" providerName="System.Data.SqlClient"/>
</connectionStrings>
//配置profile与那个数据库关联
<profile>
<providers>
<add name="AspNetSqlProfileProvider" connectionStringName="LocalSqlServer" applicationName="/" type="System.Web.Profile.SqlProfileProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"/>
</providers>
</profile>
当然,如果我们在我们的应用程序中自定义到aspnetdb得连接串,那么,我们就要修改上述machine.config中profile\provider节点下的connectionStringName的值,使其保持和我们自定义的连接串的名称一致。
下面就是测试程序
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
地址:<asp:TextBox ID="TextBox1" runat="server"></asp:TextBox><br />
邮编:<asp:TextBox ID="TextBox2" runat="server"></asp:TextBox>
<asp:Button ID="Button1" runat="server" onclick="Button1_Click" Text="Button" />
</div>
</form>
</body>
</html>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
地址:<asp:TextBox ID="TextBox1" runat="server"></asp:TextBox><br />
邮编:<asp:TextBox ID="TextBox2" runat="server"></asp:TextBox>
<asp:Button ID="Button1" runat="server" onclick="Button1_Click" Text="Button" />
</div>
</form>
</body>
</html>
using System;
using System.Collections.Generic;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
Response.Write("地址是:" + Profile.address.Trim() + ",邮政编码是:" + Profile.zip.ToString().Trim());
}
protected void Button1_Click(object sender, EventArgs e)
{
Profile.address = TextBox1.Text.Trim();
Profile.zip = Int16.Parse(TextBox2.Text.Trim());
}
}
using System.Collections.Generic;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
Response.Write("地址是:" + Profile.address.Trim() + ",邮政编码是:" + Profile.zip.ToString().Trim());
}
protected void Button1_Click(object sender, EventArgs e)
{
Profile.address = TextBox1.Text.Trim();
Profile.zip = Int16.Parse(TextBox2.Text.Trim());
}
}
注意:上述代码是在VS的website模式下测试通过的;在WebApplication下不能直接通过"Profile.属性名"来访问属性值,在此模式下的解决方案见Web Application Project 模式下的 Profile运用