ASP.NET保持用戶狀態的九種選擇

   摘要:ASP.NET爲保持用戶請求之間的數據提供了多種不同的途徑。你可以使用Application對象、cookie、hidden fields、Sessions或Cache對象,以及它們的大量的方法。決定什麼時候使用它們有時很困難。本文將介紹了上述的技術,給出了什麼時候使用它們的一些指導。儘管這些技術中有些在傳統ASP中已經存在,但是有了.NET框架組件後該在什麼時候使用它們發生了變化。爲了在ASP.NET中保持數據,你需要調整從先前的ASP中處理狀態中學習到的知識。
   隨着Web時代的到來,在無狀態的HTTP世界中管理狀態成爲Web開發者的一個大問題。最近出現了幾種存儲和檢索數據的不同技術。本文我將解釋ASP.NET開發者能怎樣通過頁面請求維護或傳遞狀態。
  在ASP.NET中,有幾種保持用戶請求間數據的途徑--實際上太多了,使沒有經驗的開發者對在哪個特定的環境下使用哪個對象很困惑。爲了回答這個問題,需要考慮下面三個條件:
   .誰需要數據?
   .數據需要保持多長時間?
   .數據集有多大?
  通過回答這些問題,你能決定哪個對象爲保持ASP.NET應用程序請求間數據提供了最佳的解決方案。圖1列出了不同的狀態管理對象並描述了什麼時候使用它們。ASP.NET中添加了四個新的對象:Cache、Context、ViewState和Web.Config文件。ASP.NET也支持傳統的ASP對象,包括Application、 Cookie、有隱藏字段的 Form Post 、 QueryString和Sessions。注意這五個數據容器的正確使用方法發生了改變,因此有經驗的程序員在考慮這些熟悉的對象時也許需要學習一些知識。

保持方法 誰需要數據 保持多長時間 數據量大小
Application 所有用戶 整個應用程序生命期 任意大小
Cookie 一個用戶 可以很短,如果用戶不刪除也可以很長 小的、簡單數據
Form Post 一個用戶 到下一次請求(可以跨越多個請求重複使用) 任意大小
QueryString 一個或一組用戶  到下一次請求(可以跨越多個請求重複使用) 小的、簡單數據
Sessions 一個用戶 用戶活動時一直保持+一段時間(一般20分鐘) 可以是任何大小,但是因爲用戶有單獨的Sessions 存儲,所有它應該最小。
Cache  所有用戶或某些用戶 根據需要 可大可小、可簡單可複雜
Context 一個用戶  一個請求 可以保持大對象,但是一般不這樣使用
ViewState  一個用戶 一個Web窗體 最小
Config file 所有用戶  知道配置文件被更新  可以保持大量數據,通常組織小的字符串和XML結構

表1. ASP.NET中的數據容器對象
  Application
  讓我們通過回答上面的狀態問題判定條件來說明該對象。誰需要數據?所有的用戶需要訪問它。需要保持數據多長時間?永久保持,或在應用程序生存期中保持。數據多大?可以是任何大小--在任何給定的時刻只有數據的一個副本存在。
  在傳統ASP中,Application對象提供了一個保存頻繁使用但很少改變的數據片的位置,例如菜單內容和參考數據。儘管在ASP.NET 中Application依然作爲數據容器存在,但是有其它一些更適合以前保存在傳統ASP應用程序的Application集合中的數據的對象。
  在傳統的ASP中,如果被保存的數據在應用程序的生存期中根本不會改變(或很少改變,例如只讀數據和大多數情況下是讀操作的數據),Application對象是理想的選擇。連接字符串就是保存在Application變量中的一個最普通的數據片,但是在ASP.NET中類似的配置數據最好保存在Web.config文件中。如果使用Application對象一個需要考慮的問題是任何寫操作要麼在Application_OnStart事件(global.asax)中,要麼在Application.Lock部分中完成。儘管使用Application.Lock來確保寫操作正確地執行是必要的,但是它串行化了對Application對象的請求,而這對於應用程序來說是個嚴重的性能瓶頸。圖2演示了怎樣使用Application對象,它包括一個Web窗體和它的代碼文件。
  Application.aspx

<form id="Application" method="post" runat="server">
<asp:validationsummary id="valSummary" Runat="server">
</asp:validationsummary>
<table>
<tr>
<td colSpan="3">Set Application Variable:</td>
</tr>
<tr>
<td>Name</td>
<td><asp:textbox id="txtName" Runat="server"></asp:textbox>
</td>
<td><asp:requiredfieldvalidator id="nameRequired"
runat="server" Display="Dynamic" ErrorMessage="Name is
required." ControlToValidate="txtName">*
</asp:requiredfieldvalidator></td>
</tr>
<tr>
<td>Value</td>
<td><asp:textbox id="txtValue" Runat="server">
</asp:textbox></td>
<td><asp:requiredfieldvalidator id="valueRequired"
Runat="server" Display="Dynamic" ErrorMessage="Value is
required." ControlToValidate="txtValue">*
</asp:requiredfieldvalidator></td>
</tr>
<tr>
<td colSpan="3"><asp:button id="btnSubmit" Runat="server"
Text="Update Value"></asp:button></td>
</tr>
</table>
<asp:Label ID="lblResult" Runat="server" />
</form>
Application.aspx.cs
private void btnSubmit_Click(object sender, System.EventArgs e)
{
if(IsValid)
{
Application.Lock();
Application[txtName.Text] = txtValue.Text;
Application.UnLock();
lblResult.Text = "The value of <b>" + txtName.Text +
"</b> in the Application object is <b>" +
Application[txtName.Text].ToString() + "</b>";
}
代碼段1.在ASP.NET中訪問Application對象
  它的輸出如下圖所示:
 
圖1. Application對象的內容
  注意圖3中Application對象的內容是追蹤輸出的顯示。追蹤是個偉大的調試工具,但是在某個點,被打開的有追蹤的頁面可能出現在產品環境中。如果出現這種情況,你肯定不希望顯示敏感的信息。這就是爲什麼Application對象從來不是推薦的存放敏感信息(例如連接字符串)的位置的主要原因之一。

  Cookies
  當特定的用戶需要特定的數據片,並且需要把數據在某個可變的時段中保持的時候,cookie就非常方便。它的生命週期可能與瀏覽器窗體的一樣短,也可以長達數月、數年。cookie可以小到只有幾個字節的數據,因爲它們在每個瀏覽器請求中傳遞,它們的內容需要儘可能的小。
  Cookie提供了一條靈活的、強大的維護用戶請求間數據的途徑,這就是爲什麼Internet上大多數動態站點使用它們的原因。因爲cookie可以存儲的數據量很受限制,最好只在cookie中保存鍵字段,其它的數據保存在數據庫或其它的服務器端數據容器中。但是由於不是所有的瀏覽器都支持cookie,並且它可以被用戶禁止或刪除,因此它們也不能用於保存關鍵數據。你應該很好地處理用戶的cookie被刪除的情況。最後,cookie作爲簡單的明文文本保存在用戶的計算機中,因此在它裏面不能保存敏感的、未加密的數據。

圖2.單值和多值cookie
  有種特殊的cookie可以保存單個值或名稱/值對的集合。圖4顯示了單個和多個值cookie的示例,通過ASP.NET的內建追蹤特性輸出。這些值可以在ASP.NET頁面中使用Request.Cookies和Response.Cookies集合來維護,這在代碼段2中演示。
  Cookies.aspx.cs
//使用HttpCookie類是指cookie的值和/或子值
HttpCookie cookie;
if(Request.Cookies[txtName.Text] == null)
cookie = new HttpCookie(txtName.Text, txtValue.Text);
else
cookie = Request.Cookies[txtName.Text];
if(txtSubValueName.Text.Length > 0)
cookie.Values.Add(txtSubValueName.Text, txtSubValueValue.Text);
cookie.Expires = System.DateTime.Now.AddDays(1); // tomorrow
Response.AppendCookie(cookie);
//檢索cookie的值
if(!Request.Cookies[txtName.Text].HasKeys)
lblResult.Text = "The value of the <b>" + txtName.Text + "</b>
cookie is <b>" + Request.Cookies[txtName.Text].Value.ToString() +
"</b>";
else
{
lblResult.Text = "The value of the <b>" + txtName.Text + "</b>
cookie is <b>" + Request.Cookies[txtName.Text].Value.ToString() +
"</b>, with subvalues:<br>";
foreach(string key in Request.Cookies[txtName.Text].Values.Keys)
{
lblResult.Text += "[" + key + " = " +
Request.Cookies[txtName.Text].Values[key].ToString() + "]<br>";
}
}
刪除Cookie
// 把的值設置爲空並把終止時間設置爲過去某個時刻
Response.Cookies[txtName.Text].Value = null;
Response.Cookies[txtName.Text].Expires =
System.DateTime.Now.AddMonths(-1); //上個月
代碼段2.Accessing 在ASP.NET中訪問Cookies
  Form Post / 隱藏的窗體字段
  特定的用戶需要窗體的數據,並且它需要在單個請求到應用程序終止的任何階段都保持。這些數據事實上可以是任意大小的,它隨着每個form post在網絡上向前和向後發送。
  在傳統的ASP中,這是在應用程序中暴露狀態的通常的途徑,特別是在多頁面窗體應用程序中。但是在ASP.NET中這種技術不太適合了,因爲只要你使用postback模型(也就是頁面發回給自己),Web控件和ViewState自動處理了這些操作。ViewState是ASP.NET對這種技術的實現,我將在本文的後部分討論它。訪問通過POST發送的窗體值是使用HttpRequest對象的窗體集合完成的。在圖6中,一個ASP.NET頁面設置了某個用戶的ID,在這以後它保持在一個隱藏的窗體字段中。後面的向任何頁面的請求保留這個值,直到頁面使用Submit按鈕鏈接到其它的用戶。
  Form1.aspx

<h1>Form 1</h1>
<form id="Application" method="post" runat="server">
<p>Your username:
<asp:Label ID="lblUsername" Runat="server" />
</p>
<asp:Panel Runat="server" ID="pnlSetValue">
<asp:validationsummary id="valSummary" Runat="server">
</asp:validationsummary>
<TABLE>
<TR>
<TD colSpan="3">Set Hidden Form Username Variable:</TD></TR>
<TR>
<TD>Username</TD>
<TD>
<asp:textbox id="txtName" Runat="server"></asp:textbox></TD>
<TD>
<asp:requiredfieldvalidator id="nameRequired" runat="server"
ControlToValidate="txtName" ErrorMessage="Name is required."
Display="Dynamic">*</asp:requiredfieldvalidator></TD></TR>
<TR>
<TD colSpan="3">
<asp:button id="btnSubmit" Runat="server" Text="Set Value">
</asp:button></TD></TR></TABLE>
</asp:Panel>
<asp:Label ID="lblResult" Runat="server" />
</form>
<form action="form2.aspx" method="post" name="form2" id="form2">
<input type="hidden" name="username" value="<%# username %>" >
<input type="submit" value="Go to Form2.aspx"
</form>
Form1.aspx.cs
private void Page_Load(object sender, System.EventArgs e)
{
if(!IsPostBack) // 新的請求或者來自form2.aspx的請求
{
// 檢查窗體集合
if(Request.Form["username"] == null)
pnlSetValue.Visible = true;
else
{
//需要設置用戶名值
pnlSetValue.Visible = false;
username = Request.Form["username"].ToString();
lblUsername.Text = username;
//數據綁定到隱藏的窗體字段值
this.DataBind();
}
}
}
private void btnSubmit_Click(object sender, System.EventArgs e)
{
if(IsValid)
{
//隱藏窗體來設置值
pnlSetValue.Visible = false;
username = txtName.Text;
lblResult.Text = "Username set to " + txtName.Text + ".";
lblUsername.Text = username;
this.DataBind();
}
}
Form2.aspx
<h1>Form 2</h1>
<form id="Application" method="post" runat="server">
<p>Your username: <asp:Label ID="lblUsername" Runat="server" /></p>
</form>
<form action="form1.aspx" method="post" id="form2" name="form2">
<input type="hidden" name="username" value="<%# username %>" >
<input type="submit" value="Go to Form1.aspx"
</form>
Form2.aspx.cs
private void Page_Load(object sender, System.EventArgs e)
{
if(Request.Form["username"] != null)
{
username = Request.Form["username"].ToString();
lblUsername.Text = username;
this.DataBind();
}
}
 
代碼段3.在ASP.NET中使用隱藏窗體字段
  在ASP.NET中一個頁面上只能存在一個服務器端窗體,並且該窗體必須提交返回到自身(仍然可以使用客戶端窗體,沒有限制)。隱藏窗體字段再也沒有用於在.NET框架組件上建立的應用程序間傳遞數據的主要原因之一是.NET框架組件控件都可以使用ViewState自動維護自己的狀態。ViewState簡單地把使用隱藏窗體字段設置和檢索值所包含的工作封裝進一個使用簡單的集合對象中。
  QueryString
  QueryString對象中保存的數據由單獨的用戶使用。它的生命週期可能只有一個請求那麼短,也可能有用戶使用應用程序的時間那麼長(如果構造正確的話)。這類數據一般小於1KB。QueryString中的數據在URL中傳遞,對於用戶來說是可見的,因此你能猜到,使用這種技術時,敏感的數據或可用於控制應用程序的數據需要加密。
  也就是說,QueryString是在ASP.NET Web窗體間發送信息的一條很好的途徑。例如,如果有一個含有產品列表的數據表格(DataGrid),並且在表格上有一個鏈接導向產品的細節頁面,使用QueryString就是理想的,可以把產品的ID包含在鏈接到產品細節頁面的QueryString中(例如productdetails.aspx?id=4)。使用QueryStrings的另一個好處是頁面的狀態包含在URL中。這意味着用戶可以把某個通過QueryStrings建立的窗體放入他的收藏夾中。當它們作爲收藏返回到頁面時,將與作收藏的時候一樣。很明顯這隻在頁面不依賴QueryString外的所有狀態和不作任何改變的時候有作用。
  敏感數據,以及任何不希望用戶操作的變量應該避免出現在此處(除非加密使用戶不能閱讀)。並且URL中不合法的字符必須使用Server.UrlEncode編碼,如圖7所示。當處理單個ASP.NET頁面時,對維護狀態來說ViewState是比QueryString好的選擇。對於長期的數據存儲,Cookie、Sessions或Cache都比QueryStrings更加適於作爲數據容器。
  Querystring.aspx

<form id="Querystring" method="post" runat="server">
<asp:validationsummary id="valSummary" Runat="server">
</asp:validationsummary>
<table>
<tr>
<td colSpan="3">Set Querystring Variable:</td>
</tr>
<tr>
<td>Name</td>
<td><asp:textbox id="txtName" Runat="server"></asp:textbox>
</td>
<td><asp:requiredfieldvalidator id="nameRequired"
runat="server" Display="Dynamic" ErrorMessage="Name is
required." ControlToValidate="txtName">*
</asp:requiredfieldvalidator></td>
</tr>
<tr>
<td>Value</td>
<td><asp:textbox id="txtValue" Runat="server">
</asp:textbox></td>
<td><asp:requiredfieldvalidator id="valueRequired"
Runat="server" Display="Dynamic" ErrorMessage="Value is
required." ControlToValidate="txtValue">*
</asp:requiredfieldvalidator></td>
</tr>
<tr>
<td colSpan="3"><asp:button id="btnSubmit" Runat="server"
Text="Update Value"></asp:button></td>
</tr>
</table>
<asp:Label ID="lblResult" Runat="server" />
<a href="querystring.aspx?x=1">Set querystring x equal to 1</a>
</form>
Querystring.aspx.cs
private void Page_Load(object sender, System.EventArgs e)
{
// 檢索cookie的值
if(Request.QueryString.HasKeys())
{
lblResult.Text = "The values of the <b>" + txtName.Text +
"</b> querystring parameter are:<br>";
foreach(string key in Request.QueryString.Keys)
{
lblResult.Text += "[" + key + " = " +
Request.QueryString[key].ToString() + "]<br>";
}
}
}
private void btnSubmit_Click(object sender, System.EventArgs e)
{
if(IsValid)
{
string url = "querystring.aspx?";
foreach(string key in Request.QueryString.Keys)
{
url += key + "=" + Request.QueryString[key].ToString() + "&";
}
Response.Redirect(url + txtName.Text + "=" +
Server.UrlEncode(txtValue.Text));
}
}
 
代碼段4.在ASP.NET中使用QueryStrings傳遞數據

  Sessions
  Sessions數據對於特定的用戶是特定的。它的生存期是用戶持續請求的時間加上後來一段時間(一般是20分鐘)。Sessions可以保持或大或小的數據量,但是如果應用程序用於成百上千的用戶,那麼總共的存儲應該保持最小。
  不幸的是在傳統的ASP中Sessions對象的名聲很不好,因爲它把應用程序約束到特定的計算機上,阻礙了用戶分組和Web範圍的可伸縮性。在ASP.NET中幾乎沒有這些問題,因爲改變Sessions保存的位置很簡單。在默認情況下(性能最好的情況),Sessions數據仍然保存在本地Web服務器的內存中,但是ASP.NET支持使用外部狀態服務器或數據庫管理Sessions數據。
  使用Sessions對象很簡單,並且它的語法與傳統ASP相同。但是Sessions對象是保存用戶數據的方法中效率很低的一種,因爲即使用戶停止使用應用程序後它仍然保持在內存中一段時間。這對於非常繁忙的站點的可伸縮性有嚴重的影響。其它的選擇允許對釋放內存的更多的控制,例如Cache對象也許更適合大量的大數據值。並且在默認情況下ASP.NET Sessionss依賴於cookie,因此如果用戶禁止或不支持cookie,Sessionss就不能工作,但是可以配置Sessionss支持cookie無關。對於小的數據量,Sessionss對象是保存只需要在用戶當前對話中保持的特定數據的極好位置。下面的例子演示了怎樣設置和從Sessionss對象中檢索值:

private void btnSubmit_Click(object sender, System.EventArgs e)
{
 if(IsValid)
 {
  // 設置Sessions值
  Sessions[txtName.Text] = txtValue.Text;
  //讀取和顯示剛纔的設置
  lblResult.Text = "The value of <b>" + txtName.Text + "</b> in the Sessions object is <b>" + Sessions[txtName.Text].ToString() + "</b>";
 }
}
 
  該Web窗體與Application對象中使用的幾乎相同,當允許頁面追蹤時Sessions集合的內容也是可見的。
你需要記住的是即使沒有使用,Sessionss也會有應用程序開銷。把Sessionss狀態設置爲只讀的也可以優化只需要讀而不需要寫數據的頁面。可以使用下面兩種途徑之一來配置Sessionss:

<%@ Page EnableSessionsstate="false" %>
<%@ Page EnableSessionsstate="readonly" %>
  ASP.NET Sessionss可以在Web.config或Machine.config中的Sessionsstate元素中配置。下面是在 Web.config中的設置的例子:

<Sessionsstate timeout="10" cookieless="false" mode="Inproc" />

  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經驗。
 
 
 
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章