ASP.NET状态存储管理九大兵器

  ASP.NET状态存储管理九大兵器

稍微经过整理,转自:http://aierong.cnblogs.com/archive/2004/07/14/23950.html

       网页状态是任何类型数据能够在一定时期内保持活跃的信息。我们这里说的一定时期可以是整个应用程序的生命周期,也可以是用户操作程序的时间,也可以是单个页面的生命周期等等。时间是有长有短的。

      由于WEB程序的HTTP协议是无状态的,所以存储状态信息就成了一个要解决的问题,既然要存储数据,那么存储的数据就需要有个存放位置,我认为只有2个地方:
·客户机
·服务器

按照存放位置进行分类,ASP。NET中状态存放方式如下:

·客户机
©查询字苻串---利用URL在客户机和服务器间进行数据交换
©隐藏的窗体字段---在窗体字段中设置和读取数据
©COOKIE---保存在客户浏览器上的数据
©视图状态---保存页面相关数据
·服务器
©应用程序---这种数据在应用程序整个生命周期内可以被所有用户利用
©会话---这种数据和每个用户联系
©暂存状态---这种数据在单个请求过程中存在
©缓存---这种数据与应用程序相类似
©其他物理数据存放媒体---例如数据库,TXT文本,XML文件等等

九大兵器之一——查询字苻串

查询字苻串是追加在URL后的数据(也是我常用的)

例如:
http://www.cnblogs.com/aierong/admin/EditPosts.aspx?opt=abc
这里?后的opt=1就是查询字符串

当我们在一页面向另一页面传递数据时可以用此方法。

使用如下方法接到数据:
string str=Request.QueryString[“opt“]
这样str就等于“abc“

这里我们传递的是英文字符,要是传递中文字符,我们得进行编码和解吗:
傳送時用Server.UrlEncode方法編碼,接收時用Server.UrlDecode解碼.

例如:
传递:
string url=“http://www.cnblogs.com/aierong/admin/EditPosts.aspx?opt=“+Server.UrlEncode(“我们“);
Response.Redirect(url);
接收:
string str=Server.UrlDecode(Request.QueryString[“opt“]);
这样str就等于“我们“

当然我们还有一省事的方法:

在web.config中修改globalization节为
<globalization
            requestEncoding="gb2312"
            responseEncoding="gb2312"
/>

总结,查询字苻串只可以传递少量数据,因为HTTP WEB服务器不能处理超过255个字符的查询字苻串,查询字苻串最好不要传递安全性高的数据,例如不要传递密码,银行卡号码等数据

九大兵器之二——隐藏的窗体字段

利用隐藏的窗体字段在客户机和服务器间传递数据也是可行的

例如:
<input type="hidden" name="aa" value="abc">

string str=Request.Form["aa"];
str就等于“abc“

总结,最好不要放安全性高的数据,例如不要传递密码,银行卡号码等数据

九大兵器之三——暂存状态

ASP.NET提供一个类System.Web.HttpContext ,用来表示上下文,此对象有一个属性Items

暂存状态就是利用HttpContext.Items属性来存放数据

MSDN中HttpContext.Items属性的解释是这样的:获取可用于在 HTTP 请求过程中在 IHttpModule 和 IHttpHandler 之间组织和共享数据的键值

HttpContext.Items属性中可以存放任何类型数据,无论这个属性中存放什么数据,都会在请求处理结束后自动清除,这就是暂存状态,数据的存放时间非常短.


// 例如:
// 我们有一页面A.ASPX,有一按钮ID:Submit,点按钮把页面转向b.aspx页面

public   void  Submit_Click(Object sender, EventArgs E)
{
    SqlConnection myConnection 
= new SqlConnection("server=(local)NetSDK;database=pubs;Trusted_Connection=yes");
    SqlDataAdapter myCommand 
= new SqlDataAdapter("select * from Authors", myConnection);
    DataSet ds 
= new DataSet();
    myCommand.Fill(ds, 
"Authors");
    
//把数据放入暂存中
    Context.Items["MyData"=ds;
    Server.Transfer(b.aspx);    
}



// b.aspx页面中

public   void  Page_Load(Object sender, EventArgs E)
  
{
    
if (!IsPostBack)
    
{
    
//取得暂存数据
    DataSet ds=(DataSet)Context.Items["MyData"];
    
//其它数据过程
    }

  }

在ibuyspyportal中我们也看到了此功能的使用:

查询字符串包含正被请求的选项卡的 TabIndedx 和 TabId 参数。在处理请求的整个过程中,一直使用此信息筛选要显示给用户的数据。
http://www.ibuyspyportal.com/DesktopDefault.aspx?tabindex=1&tabid=2
要使用查询字符串值,需要首先确保它是一个有效值,如果不是,则要进行一些错误处理。它并不是一大串代码,但是您真的要在每个使用该值的页和组件中复制它吗?当然不!在 Portal 示例中,甚至更多的地方都涉及到它,因为一旦我们知道了 TabId,就可以预先加载其他信息。

Portal 使用查询字符串值作为参数,以构造一个新的 PortalSettings 对象,并将它添加到 Global.asax 的 BeginRequest 事件的 Context.Items 中。由于在每个请求开始处都执行了开始请求,这使得与该选项卡有关的值在应用程序的所有页和组件中都可用。请求完成后,对象将被自动丢弃


void  Application_BeginRequest(Object sender, EventArgs e)
 

     
  
int tabIndex = 0
  
int tabId = 0

  
// 从查询字符串获取 TabIndex 

  
if (Request.Params["tabindex"!= null{        
    tabIndex 
= Int32.Parse(Request.Params["tabindex"]); 
  }
 
         
  
// 从查询字符串获取 TabID 

  
if (Request.Params["tabid"!= null{        
    tabId 
= Int32.Parse(Request.Params["tabid"]); 
  }
 

  Context.Items.Add(
"PortalSettings"new PortalSettings(tabIndex, tabId)); 
}
 

DesktopPortalBanner.ascx 用户控件从 Context 请求 PortalSetting 的对象,以访问 Portal 的名称和安全设置。事实上,此模块是操作中的 Context 的一个典型综合示例。为阐明这一点,我已将代码进行了一些简化,并用粗体标记了 HTTP 或应用程序特定的 Context 被访问过的所有地方。

<% @ Import Namespace="ASPNetPortal"  %>  
<% @ Import Namespace="System.Data.SqlClient"  %>  

< script  language ="C#"  runat ="server" >  

  public 
int     tabIndex; 
  public bool     ShowTabs 
= true
  protected String  LogoffLink 
= ""

  
void Page_Load(Object sender, EventArgs e) 

    
// 从当前上下文获取 PortalSettings 
 PortalSettings portalSettings = 
(PortalSettings) Context.Items[
"PortalSettings"]; 

    
// 动态填充门户站点名称 
    siteName.Text = portalSettings.PortalName; 

    
// 如果用户已登录,自定义欢迎信息 
    if (Request.IsAuthenticated == true
     
      WelcomeMessage.Text 
= "欢迎" + 
Context.User.Identity.Name 
+ "!<" + 
"span class=Accent" + ">|<" + "/span" + ">"

      
// 如果身份验证模式为 Cookie,则提供一个注销链接 
      if (Context.User.Identity.AuthenticationType == "Forms"
        LogoffLink 
= "<" + "span class="Accent">|</span> " + 
"<a href=" + Request.ApplicationPath + 
"/Admin/Logoff.aspx class=SiteLink> 注销" + 
"</a>"
      }
 
    }
 

    
// 动态显示门户选项卡条 
    if (ShowTabs == true

      tabIndex 
= portalSettings.ActiveTab.TabIndex; 

      
// 生成要向用户显示的选项卡列表                  
      ArrayList authorizedTabs = new ArrayList(); 
      
int addedTabs = 0

      
for (int i=0; i < portalSettings.DesktopTabs.Count; i++
       
        TabStripDetails tab 
= 
(TabStripDetails)portalSettings.DesktopTabs[i]; 

        
if (PortalSecurity.IsInRoles(tab.AuthorizedRoles)) 
          authorizedTabs.Add(tab); 
        }
 

        
if (addedTabs == tabIndex) 
          tabs.SelectedIndex 
= addedTabs; 
        }
 

        addedTabs
++
      }
      

      
// 用已授权的选项卡填充页顶部的选项卡 
//
 列表 
      tabs.DataSource = authorizedTabs; 
      tabs.DataBind(); 
    }
 
  }
 

</ script >  
< table  width ="100%"  cellspacing ="0"  class ="HeadBg"  border ="0" >  
  
< tr  valign ="top" >  
    
< td  colspan ="3"  align ="right" >  
      
< asp:label  id ="WelcomeMessage"  runat ="server"   />  
      
< href ="<%= Request.ApplicationPath %>" > Portal 主页 </ a >  
< span  class ="Accent" >  | </ span >  
< href ="<%= Request.ApplicationPath %>/Docs/Docs.htm" >  
        Portal 文档
</ a >  
      
<% =  LogoffLink  %>  
         
    
</ td >  
  
</ tr >  
  
< tr >  
    
< td  width ="10"  rowspan ="2" >  
        
    
</ td >  
    
< td  height ="40" >  
      
< asp:label  id ="siteName"  runat ="server"   />  
    
</ td >  
    
< td  align ="center"  rowspan ="2" >  
     
    
</ td >  
  
</ tr >  
  
< tr >  
    
< td >  
      
< asp:datalist  id ="tabs"  runat ="server" >  
        
< ItemTemplate >  
           
< href ='<%=  Request.ApplicationPath % >  
/DesktopDefault.aspx?tabindex=
<% # Container.ItemIndex  %> &tabid
<% # ((TabStripDetails) Container.DataItem).TabId  %> '> 
<% # ((TabStripDetails) Container.DataItem).TabName  %>  
</ a >   
        
</ ItemTemplate >  
        
< SelectedItemTemplate >  
           
         
< span  class ="SelectedTab" >  
<% # ((TabStripDetails) Container.DataItem).TabName  %>  
</ span >   
        
</ SelectedItemTemplate >  
      
</ asp:datalist >  
    
</ td >  
  
</ tr >  
</ table >  
 

Cookie是一段文本信息,在客户端存储 Cookie 是 ASP.NET 的会话状态将请求与会话关联的方法之一。Cookie 也可以直接用于在请求之间保持数据,但数据随后将存储在客户端并随每个请求一起发送到服务器。浏览器对 Cookie 的大小有限制,因此,只有不超过 4096 字节才能保证被接受。

编写Cookie


// 方式1:
Response.Cookies[ " username " ].value = " mike " ;
Response.Cookies[
" username " ].Expires = DateTime.MaxValue; 

// 方式2:
HttpCookie acookie  =   new  HttpCookie( " last " );
acookie.Value
= " a " ;
acookie..Expires
= DateTime.MaxValue; 
Response.Cookies.Add(acookie);

// 多值Cookie的写法

// 方式1:
Response.Cookies[ " userinfo1 " ][ " name " ].value = " mike " ;
Response.Cookies[
" userinfo1 " ][ " last " ].value = " a " ;
Response.Cookies[
" userinfo1 " ].Expires = DateTime.MaxValue; 

// 方式2:
HttpCookie cookie  =   new  HttpCookie( " userinfo1 " );
cookie.Values[
" name " ] = " mike " ;
cookie.Values[
" last " ] = " a " ;
cookie.Expires
= DateTime.MaxValue; 
Response.Cookies.Add(cookie);
读取Cookie
Internet Explorer 将站点的 Cookie 保存在文件名格式为 <user>@<domain>.txt 的文件中,其中 <user> 是您的帐户名。
注意:在获取Cookie的值之前,应该确保该 Cookie 确实存在。否则,您将得到一个异常

运行此代码时,可看到一个名为“ASP.NET_SessionId”的Cookie,ASP.NET用这个 Cookie 来保存您的会话的唯一标识符。
修改 Cookie
修改的方法与创建方法相同
删除 Cookie 
将其有效期设置为过去的某个日期。当浏览器检查 Cookie 的有效期时,就会删除这个已过期的 Cookie。

If (Request.Cookies[
" userName " ] != null )
{
  
string str = Request.Cookies("userName").Value; 
}


// 多值Cookie的读取
If ( Request.Cookies[ " userInfo1 " ] != null  )
{
  
string name=Request.Cookies["userInfo1"]["name"];
  
string last=Request.Cookies["userInfo1"]["last"]; 
}



// 读取 Cookie 集合
for ( int  i  =   0  ;i < Request.Cookies.Count ;i ++ )
{
    HttpCookie cookies 
= Request.Cookies[i];
    Response.Write(
"name="+cookies.Mame+"<br>");
    
if (cookies.HasKeys )//是否有子键
    {
        System.Collections.Specialized.NameValueCollection NameColl 
                                             = aCookie.Values ;
        
for(int j=0;j<NameColl.Count;j++)
        
{
            Response.Write(
"子键名="+ NameColl.AllKey[j] +"<br>");
            Response.Write(
"子键值="+ NameColl[j] +"<br>");
        }


    }

    
else
    
{
        Response.Write(
"value="+cookies.Value+"<br>");        
    }

}




 


HttpCookie cookie 
=   new  HttpCookie( " userinfo1 " );
cookie.Expires
= DateTime.Now.AddDays( - 30 ); 
Response.Cookies.Add(cookie);
 
九大兵器之五——缓存

ASP.NET 提供一个功能完整的缓存引擎,页面可使用该引擎通过 HTTP 请求存储和检索任意对象.
缓存的生存期与应用程序的生存期相同,也就是说,当应用程序重新启动时,将重新创建缓存。

将数据添加到缓存中

1。通过指定其键和值将项添加到缓存中
Cache["txt"] = "a";

2.通过使用 Insert(重载Insert方法)方法将项添加到缓存中

Cache.Insert("txt", "a");

下列代码显示如何设置相对过期策略。它插入一个项,该项自上次访问后 10 分钟过期。注意 DateTime.MaxValue 的使用,它表示此项没有绝对过期策略。

DateTime absoluteExpiration=DateTime.MaxValue;
TimeSpan slidingExpiration=TimeSpan.FromMinutes(10);
Cache.Insert("txt", "a",null,
absoluteExpiration,slidingExpiration,
System.Web.Caching.CacheItemPriority.High,null);

3.使用 Add 方法将项添加到缓存中
Add 方法与 Insert 方法具有相同的签名,但它返回表示您所添加项的对象

DateTime absoluteExpiration=DateTime.MaxValue;
TimeSpan slidingExpiration=TimeSpan.FromMinutes(10);
Object  Ojb=(string)Cache.Add("txt","a",null,
absoluteExpiration ,slidingExpiration ,
System.Web.Caching.CacheItemPriority.High,null);
string str=(string)Ojb ;
Response.Write(str);

结果显示是"a"

Add 方法使用上没有Insert 方法灵活,使用Add 方法时必须提供7个参数,Insert 方法重载4次,我们可以根据需要选择适当重载方法

从缓存中取得数据

方式1:
string str=(string)Cache.Get("txt");
Response.Write(str);

方式2:
string str1=(string)Cache["txt1"];
Response.Write(str1);

查看Cache中所有数据

System.Text.StringBuilder sb=new System.Text.StringBuilder("",100);
foreach(DictionaryEntry Caches  in Cache)
{
sb.Append("key=").Append(Caches.Key.ToString()).Append("<br>") ;
sb.Append("value=").Append(Caches.Value.ToString()).Append("<br>");
}
Response.Write(sb.ToString());

查看Cache中的项数

int Count=Cache.Count;
Response.Write(Count.ToString());


将数据从缓存中删除

Cache.Remove("txt");

使Cache具有文件依赖项或键依赖项的对象

我们在一页面建立1个按钮,查看CACHE是否存在
在窗体启动时我们创建CACHE,名称="txt2",数值=数据集ds
该CACHE与myfile.xml相关联,当myfile.xml文件变化时,txt2CACHE就被自动删除

private void Page_Load(object sender, System.EventArgs e)
  {
   if( !IsPostBack  )
   {
   string FilePath=MapPath("myfile.xml");
   SqlConnection con=new SqlConnection("Uid=sa;database=pubs;");
   SqlDataAdapter da =new SqlDataAdapter("select * from authors",con);
   DataSet ds=new DataSet();
   da.Fill(ds);
   System.Web.Caching.CacheDependency CacheDependencyXmlFile=new System.Web.Caching.CacheDependency(FilePath);
   Cache.Insert("txt2",ds ,CacheDependencyXmlFile);
   }
  }


为了监视pubs数据库authors表的变化
我们可以在pubs数据库authors表建立触发器
一旦authors表中发生增加,删除,修改数据时,触发器会自动修改文件myfile.xml
一旦myfile.xml文件变化,txt2CACHE就被系统自动删除

CREATE TRIGGER tr_authors
ON authors
FOR INSERT, UPDATE ,delete
AS
declare @cmd nvarchar(4000)
select @cmd='bcp "select convert(nvarchar(30),Getdate(),13)" queryout D:/cache/WebCache/myfile.xml -c -Sglc2403 -Usa -P'
exec master..xp_cmdshell @cmd
GO


private void QueryButton_Click(object sender, System.EventArgs e)
{
if ( Cache["txt2"]!=null)
{
 Response.Write("exists");
}
else
{
 Response.Write("not exists");
}
}

首先我们点按钮,显示Cache["txt2"]存在
现在我们去表authors中任意修改一数据,再点按钮,显示Cache["txt2"]不存在拉


以上我们是把CACHE是和一个文件相关联,我们还可以把CACHE和文件组关联,建立依赖
以下我们把CACHE和2个文件(myfile.xml,myfile1.xml)关联,一旦文件组中其中任意一文件变化,Cache会把"txt2"项的数据从CACHE中删除

string[] FilePath=new String[]{MapPath("myfile.xml"),MapPath("myfile1.xml")};
System.Web.Caching.CacheDependency CacheDependencyXmlFile=new                     System.Web.Caching.CacheDependency(FilePath);
string CacheVaule="a";
Cache.Insert("txt2", CacheVaule ,CacheDependencyXmlFile);


缓存依赖可以是文件,还可以是其他对象的键
下面的代码说明缓存Cache["txt2"]既依赖文件myfile.xml,又依赖缓存中的Cache["txt"],只要这2者任意一样改变,缓存Cache["txt2"]就会清除

Cache["txt"] = "b";
string[] FilePath=new String[]{ MapPath("myfile.xml")};
string[] dependencyKey=new String[]{"txt"};
SqlConnection con=new SqlConnection("Uid=sa;database=pubs;");
SqlDataAdapter da =new SqlDataAdapter("select * from authors",con);
DataSet ds=new DataSet();
da.Fill(ds);
System.Web.Caching.CacheDependency CacheDependencyXmlFile=
          new System.Web.Caching.CacheDependency(FilePath,dependencyKey);
Cache.Insert("txt2",ds ,CacheDependencyXmlFile);

缓存绝对过期

缓存Cache["txt3"] 在1小时后自动过期
DateTime absoluteExpiration =DateTime.Now.AddHours(1);
Cache.Insert("txt3","aa",null,absoluteExpiration,System.Web.Caching.Cache.NoSlidingExpiration);

缓存相对(滑动)过期

注意:如果创建的弹性到期时间小于零或大于一年,则将引发异常
缓存Cache["txt4"] 在最后一次被访问后1小时自动过期
TimeSpan slidingExpiration=TimeSpan.FromHours(1);
Cache.Insert("txt4","4",null,System.Web.Caching.Cache.NoAbsoluteExpiration,slidingExpiration);


缓存项的优先等级

当承载 ASP.NET 应用程序的 Web 服务器缺少内存时,Cache 将有选择地清除项来释放系统内存。当向缓存添加项时,可以为其分配与缓存中存储的其他项相比较的相对优先级。在服务器处理大量请求时,分配了较高优先级值的项被从缓存删除的可能性较小,而分配了较低优先级值的项则更有可能被删除。
由CacheItemPriority 枚举表示,默认为 Normal。

缓存Cache["txt5"]优先等级设为最高等级,在服务器释放系统内存时,该缓存项最不可能被删除。
Cache.Insert("txt5","5",null,
System.Web.Caching.Cache.NoAbsoluteExpiration,
System.Web.Caching.Cache.NoSlidingExpiration,
System.Web.Caching.CacheItemPriority.High,null);

缓存项时通知应用程序的回调方法

ASP.NET 提供 CacheItemRemovedCallback 委托。它定义编写事件处理程序时使用的签名,当从缓存中删除项时,该事件处理程序将进行响应。

static System.Web.Caching.CacheItemRemovedReason reason;
System.Web.Caching.CacheItemRemovedCallback onRemove = null;

public void RemovedCallback(String k, Object v, System.Web.Caching.CacheItemRemovedReason r)
{
 itemRemoved = true;
 reason = r;
}

onRemove = new System.Web.Caching.CacheItemRemovedCallback (this.RemovedCallback);
Cache.Insert("txt",ds,null,
System.Web.Caching.Cache.NoAbsoluteExpiration,
System.Web.Caching.Cache.NoSlidingExpiration,
System.Web.Caching.CacheItemPriority.Default,onRemove);

由于任何原因从Cache中移除时,将调用 RemovedCallback 方法

九大兵器之六——其它物理数据存放媒体

ASP.NET状态存储还可以存放在数据库,XML文件,文本文件,注册表中等等,物理数据存放媒体

数据库
首推ADO.NET

XML
管理XML文档和流主要由System.Xml命名空间中类执行

文件
相关类在System.IO命名空间中
注册表操作由System.Win32命名空间中2个类执行

九大兵器之七——应用程序

可以使用 HttpApplicationState 类在整个应用程序中共享信息,该类通常是通过 HttpContext 对象的 Application 属性进行访问的。该类公开对象的键/值字典,您可以使用该字典来存储 .NET 框架对象和与来自多个客户端的多个 Web 请求相关的标量值。
创建Application
private void Page_Load(object sender, System.EventArgs e)
{
 if( !IsPostBack  )
 {
  SortedList  ApplicationValue =new SortedList();
  ApplicationValue.Add("1","a");
  ApplicationValue.Add("2","b");
  ApplicationValue.Add("3","c");
  ApplicationValue=SortedList.Synchronized(ApplicationValue);
  Context.Application["app"]=ApplicationValue;
  /*
  设置Application的第2种方法
  Add方法,将新的对象添加到 HttpApplicationState 集合中
  Context.Application.Add("app",ApplicationValue);
  */
 }
}
读取Application
private void Button1_Click(object sender, System.EventArgs e)
{
 SortedList  List=(SortedList)Context.Application["app"];
 /*
 取得Application的第2种方法
 Get方法已重载。通过名称或索引获取 HttpApplicationState 对象
 SortedList  List=(SortedList)Context.Application.Get("app");
 或者
 SortedList  List=(SortedList)Context.Application.Get(0);
 */
 System.Text.StringBuilder sb=new System.Text.StringBuilder("");
 if( List!=null )
 {
  sb.Append("app exists").Append("<br>") ;
  for(int i=0;i<List.Count;i++)
  {
   sb.Append((string)List.GetKey(i)).Append("      ");
   sb.Append((string)List.GetByIndex(i)).Append("<br>");
  }
  Response.Write(sb.ToString());
 }
 else
 {
  Response.Write("app not exists");
 }
}
点击按钮后,显示
app exists
Key value
1 a
2 b
3 c
应用程序状态同步

应用程序中的多个线程可以同时访问存储在应用程序状态中的值。因此,当创建需要访问应用程序状态值的对象时,必须始终确保该应用程序状态对象是自由线程的并执行它自己的内部同步,要不就执行手动同步步骤以防止出现争用条件、死锁或访问冲突。

HttpApplicationState 类提供两种方法 Lock 和 Unlock,一次只允许一个线程访问应用程序状态变量。

对 Application 对象调用 Lock 会导致 ASP.NET 阻止运行在其他辅助线程上的代码试图访问应用程序状态中的任何对象。只有当调用 Lock 的线程对 Application 对象调用相应的 Unlock 方法时才解除对这些线程的阻塞。

Application.Lock();
Application["count"]=(int)Application["count"]+1;
Application.UnLock();

如果没有显式调用 Unlock,当请求完成、请求超时或请求执行过程中出现未处理的错误并导致请求失败时,.NET 框架将自动移除锁。这种自动取消锁定会防止应用程序出现死锁。


应用程序需要小心使用复杂对象,例如,集合.集合没有被设计为供多线程同时访问.
利用方法Synchronized()创建集合对象线程安全版本

SortedList  ApplicationValue =new SortedList();
ApplicationValue.Add("1","a");
ApplicationValue.Add("2","b");
ApplicationValue.Add("3","c");
ApplicationValue=SortedList.Synchronized(ApplicationValue);

[END]

ASP.NET保持用户状态的九种选择(下)
ASP.NET中的新状态容器

  前面我们提到,ASP.NET为保存用户请求间的数据添加了几种新的途径。这些途径给了你如何保持状态信息更好的控制。这些技术的范围可以窄到只有一个请求那么小(Context对象),也可以宽到整个Web服务器和服务器上的所有应用程序(Machine.config文件)。在多数情况下你有多种保存特定数据片的选择--使用每个方法描述的问题和答案来决定某个对象是否适合你的需要。

  Cache

  Cache对象用于单个用户、一组用户或所有的用户。这种数据为多个请求保持。它可以保持很长时间,但是不能超过应用程序重新启动的时间,并且数据的终止基于时间或者其它的依赖关系。它可以高效率地保持大量或少量地数据。

  Cache 是ASP.NET中最"酷"的对象之一。它提供了难以置信的灵活性、通用性和性能,因此在ASP.NET应用程序中它通常是比Application或Sessions更好的保持数据的对象。本文没有详细介绍Cache对象的使用方法,但是仍然可以说它是一个万能对象。与其它的集合对象相似,它是一个简单的名称-值集合,但是通过使用指定特定用户的键值可以缓存特定用户的值。同样你可以缓存不同的相关数据的多个数据集,例如几个有键(如fordcars 、 chevycars、gmcars)的汽车集合。Cache中的数据可以给定一个绝对的、可变的或基于文件的终止时间。它们也实现了一个回调功能,在被缓存的值从缓存中提取时被调用,这个功能很有用,因为接着你能检查它是否为最新的数据变量,如果不是(或数据源不可用),就重新缓存被终止的值。

  添加和访问缓存中值的语法与先前谈到的相似。但是Cache给访问集合内容的标准索引器方法作了补充,它支持多种方法,允许对被缓存数据的更多的控制。最频繁使用的方法是Insert,它支持几种重载,允许你指定依赖、超时值、优先级和回调。下面是一些简单的例子:


// 给缓存添加项
Cache["myKey"] = myValue;

// 从缓存中读取项
Response.Write(Cache["myKey"]);

// 把CacheDuration增加10秒并把项添加到缓存中
Cache.Insert("myKey",myValue, null, System.DateTime.Now.AddSeconds(10),
System.Web.Caching.Cache.NoSlidingExpiration);
 

  Cache对象的最强大的特性之一是当缓存中的某个项终止时执行回调的能力。它使用了委托或函数指针,这在本文中没有讨论。幸运的是一旦你有了某些这些技术怎样工作的示例,就能通过简单的剪切和粘贴在应用程序中使用它们,不需要知道委托是怎样工作的复杂过程。有很多使用这种功能的原因,最通常的是在数据终止时用当前数据重新填充缓存,或者如果重新填充缓存的数据源不可用时恢复旧的缓存数据。

  在我的例子中,简单地缓存了当前时间,当缓存超期的时候,我将给缓存中的字符串末尾添加一个星号(*)。在超过时间后,你能通过计算星号的数量来确定缓存超期了多少次。图9演示了回调的重要概念,并且提供了给使用缓存建立更多功能回调程序的好模板。


private void Page_Load(object sender, System.EventArgs e)
{
 string cacheKey = "myKey";
 string data = "";
 // 检查数据是否已经被缓存了
 if(Cache[cacheKey]==null)
 {
  // 因为数据在缓存中,所有读取数据
  data = System.DateTime.Now.ToString();

  //建立回调委托的一个实例
  CacheItemRemovedCallback callBack =new CacheItemRemovedCallback(onRemove);

  Label1.Text = "Generated: " + data;

  Cache.Insert(cacheKey,data,null,
      System.DateTime.Now.AddSeconds(5),
      System.Web.Caching.Cache.NoSlidingExpiration,
      System.Web.Caching.CacheItemPriority.Default,
      callBack);
 }
 else
 {
  Label1.Text = "Cached: " + Cache[cacheKey].ToString();
 }
}

private void onRemove(string key, object val,CacheItemRemovedReason reason)
{
 //建立回调委托的一个实例
 CacheItemRemovedCallback callBack =new CacheItemRemovedCallback(onRemove);
    Cache.Insert(key,val.ToString() +
    "*",null,System.DateTime.Now.AddSeconds(5),Cache.NoSlidingExpiration,
    System.Web.Caching.CacheItemPriority.Default, callBack);
}
 

代码段5.缓存回调示例

  注意代码段中一个重要的特性是在Page_Load中使用模式(pattern)来确定是否使用缓存中的数据。当你处理缓存中的项时也可能使用这种模式。使用if语句来检查缓存的当前内容是否为空(因为要多次引用,为缓存键使用了一个变量)。如果是空的,从数据源生成数据并放入缓存中。如果不是空的,从缓存中返回数据。如果数据访问逻辑很复杂,你需要把整个if语句放入一个独立的函数,该函数的任务是检索数据。
Cache对象的功能比先前我们讨论的大多数对象多得多。这也是ASP.NET更强大的功能之一,并且我明确地推荐阅读关于它的更多内容。

  Context

  Context对象保持单个用户、单个请求的数据,并且数据只在该请求期间保持。Context容器可以保持大量的数据,但是典型的情况下是保存小的数据片,因为它经常通过global.asax中的某个处理方法为每个请求实现。

  Context容器(从Page对象访问或使用System.Web.HttpContext.Current)被提供用于保持需要在不同的HttpModules和HttpHandlers之间传递的值。它也可以用于保持某个完整请求的相应信息。例如,IbuySpy入口在global.asax中的Application_BeginRequest事件过程中给容器填满了许多配置信息。注意这只在当前请求中可用,如果你希望在下一个请求中也能使用,请考虑使用ViewState。

  从Context集合中设置和获取数据使用的语法与前面讨论的其它集合对象(如Application、Sessions和 Cache)的相似。下面是两个简单的例子:


// 给Context添加项
Context.Items["myKey"] = myValue;

// 从Context中读取项
Response.Write(Context["myKey"]);

 

  ViewState

  ViewState为单个用户保持状态信息,保持期为ASPX页面工作时间。ViewState容器可以保持大量的数据,但是必须小心管理ViewState的大小,因为它增加了每个请求和回应的下载(download)大小。

  ViewState是ASP.NET中的一个新容器,也许你已经使用它了,但是你可能还是不了解它。这是因为所有的内建Web控件都使用ViewState在页面回发(postback)间保持自己的值。但是你必须小心,因为它影响应用程序的性能。影响的大小依赖于回发之间使用ViewState的多少--对大多数Web窗体来说数量非常小。

  确定某个页面上每个控件使用的ViewState的数量最简单的方法是打开页面追踪并检查每个控件负载了多少个ViewState。如果某个特定控件不需要在回发之间保持数据,请通过把EnableViewState设置为false关闭该对象的ViewState。你也可以通过在浏览器中查看的HTML源并检查隐藏窗体字段__VIEWSTATE来确定某个给定的ASP.NET页面ViewState的总共大小。注意这些内容都是使用Base64编码的,用于放置偶然的查看和维护。ViewState也可以通过给@Page指令添加EnableViewState="false"在整个页面中禁止。

  典型的Web窗体不需要直接维护ViewState。但是如果你建立自定义Web控件,就需要了解它是怎样工作的,并为你的控件实现它,这样该控件的工作方式才能与随ASP.NET发布的Web控件同样地工作。向ViewState读取或写入值都可以通过上面讨论地其它集合对象的语法完成:


// 给ViewState添加项
ViewState["myKey"] = myValue;

//从Context读取项
Response.Write(ViewState["myKey"]);
 

  当建立自定义Web控件时,你也许希望它们有ViewState的好处。这在控件的属性层可以简单实现。代码段6演示了怎样保存一个简单的自定义控件的PersonName属性到ViewState中,并在该控件的Render方法中使用它。


namespace MSDN.StateManagement
{
 public class HelloPerson : System.Web.UI.Control
 {
  public string PersonName
  {
   get
   {
    string s = (string)ViewState["PersonName"];
    return ((s == null) ? "" : s);
   }
   set
   {
    ViewState["PersonName"] = value;
   }
  }
  protected override void Render(System.Web.UI.HtmlTextWriter writer)
  {
   writer.Write("Hello " + PersonName);
  }
 }
}

代码段6.在ViewState中保存数据

  Web.config和Machine.config文件

  这些文件中的数据对于某个应用程序的所有用户来说都可以使用。Web.config文件中存储的数据可用于应用程序的整个生命周期。这些数据一般很小,该对象一般用于保持文件位置和数据库连接的字符串。大的数据片最好保存在其它位置。

  作为其它多样集合对象的补充,ASP.NET引入了一组XML配置文件用于管理应用程序甚至于整个服务器的很多设置。每个ASP.NET应用程序使用Web.config文件来设置它的许多属性,每个服务器在系统文件夹下有一个作为应用程序基础的Machine.config文件。这些设置都作为默认值使用,除非重载。作为保存配置数据的补充,这些文件可以保存应用程序(或多个应用程序)需要的数据。

  无论什么时候应用程序启动都会读取配置信息,接着这些信息被缓冲。由于被缓冲了,应用程序可以快速读取它们,因此不需要考虑应用程序的瓶颈,因为它经常执行某个文本文件的一些整型信息。此外,某个应用程序的Web.config的改变将导致应用程序重新启动。这确保了对配置文件信息的修改立即反映到应用程序中。

  数据库连接信息,默认图像路径和XML数据文件路径是通常保存在Web.config文件中的数据片。在Web.config文件中保存数据的语法如下,在理想的情况下你也许希望使用集成的SQL身分验证:


<configuration>
<!-应用程序特殊设置 -->
<appSettings>
<add key="connectionString" value="server=myDBServer;
uid=myUID;pwd=myPassword;database=myDB" />
</appSettings>
<system.web>
<!-所有的wsb设置 -->
</system.web>
</configuration> 

  为了访问ASP.NET页面中的值,可以使用ConfigurationSettings集合,它在System.Configuration名字空间中。下面的简单例子演示了怎样提取前面的连接字符串到一个本地变量中:

using System.Configuration;
ooo
String strConnString =
ConfigurationSettings.AppSettings["connectionString"];

  给System.Configuration名字空间添加一个引用减少了引用这些值的代码数量。因为对Web.config或 Machine.config的修改将导致应用程序立即重新启动,典型情况下这些值只由服务器系统管理员手动修改。因此你可以认为这些文件是保存只读数据而不是应用程序中修改的数据的好位置。

  结论

  有效的状态管理意味着识别的用户经验、数据错误与快速的页面或事务处理之间的巨大差别。尽管状态管理在ASP 3.0中不太适用,但是ASP.NET把它带到了本文讨论的状态对象的控制之下。小心地使用它们将使你给用户展示最佳的Web经验。

九大兵器之四——Cookie
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值