ViewState使用

ASP.NET頁面狀態管理——ViewState的使用

ASP.NET ViewState設計目的是爲了持久化當前頁面中的對象的狀態,以便下次在頁面回發(Postback)後能夠還原頁面的狀態。那麼有兩點需要注意:
  1. ViewState只在需要Postback的頁面裏才需要使用;
  2. 在1前提之下,只有初始狀態值被修改了的對象才需要持久化,即才需要使用ViewState。

1比較清楚,來談第2點。以簡單的Label控件爲例,先來看一下它的Text屬性的實現:

public virtual string Text
{
    get
    {
        object obj2 = this.ViewState["Text"];
        if (obj2 != null)
        {
            return (string) obj2;
        }
        return string.Empty;
    }
    set
    {
        if (this.HasControls())
        {
            this.Controls.Clear();
        }
        this.ViewState["Text"] = value;
    }
}

很顯然Text屬性的後端都是以ViewState爲存儲介質的,ASP.NET服務器端控件的很多屬性都是以類似方式實現的。假設一個頁面default.aspx裏只有一個Label控件, <asp:Label ID="Label1" runat="server" Text="Label"></asp:Label>,當訪問該頁面時,Label控件發送到客戶端瀏覽器的代碼大概爲 <span id="xxx_Label1">Label</span>,同時ViewState中也保存了一份Text屬性的值,形式大概爲<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwUKLTgwMzg2ODMxMQ9kFgJmD2QWAgIDD2QWAg......
到目前位置,ViewState並沒有發揮作用(這裏談論的都是在ViewState Enable的情況下),額外的保存了Text屬性的值在這裏是多餘的。假設該頁面還有一個Button控件,點擊該按鈕頁面回發,那麼在次過程中ViewState就發揮作用了麼?分析一下該頁面回發過程中Label控件生命週期的某些過程。首先,回發後,Label控件依然要經歷初始化階段(Init),這個階段要創建一個Label控件的實例,同時設置其Text屬性的,因爲Text屬性後端是以ViewState爲存儲介質的,所以就相當於向ViewState裏添加了一個值,接下來,因爲是回發過程,所以控件還要LoadViewState,即加載前次訪問該控件的狀態值,下面是Label控件的實現:

protected override void LoadViewState(object savedState)
{
    if (savedState != null)
    {
        base.LoadViewState(savedState);
        string text = (string) this.ViewState["Text"];
        if (text != null)
        {
            this.Text = text;
        }
    }
}
因爲在前一訪問過程中ViewState中所保存的Label的Text屬性的狀態值就是Label的初始值,所以導致了這裏的LoadViewSate過程是多餘的了,而且Init和LoadViewState兩個過程對Text屬性都賦了相同的值。由此可見,即使在頁面回發中,如果不需要對屬性的初始值進行修改,那麼持久化屬性的值(即使用ViewState)也是沒有意義的。而且會帶來多餘的資源浪費,如兩次對Text屬性的賦值,以及增加了ViewSate的體積所帶來的多餘的網絡傳輸。

那麼,什麼情況下使用ViewSate是值得的呢?我們先來把前面例子中兩次訪問的過程理一下:

  1. 第一次訪問頁面,Label控件初始化,設置Text屬性的值,即向ViewState中添加了一個條目;
  2. 頁面發送前(Render前),控件SaveViewState,即ViewSate中的值序列化,保存到一個隱藏域中;
  3. 頁面發送,Label控件發送爲相應的HTML標籤,讀取Text屬性設置HTML標籤的對象屬性值,同時發送隱藏域及其值。對於Labe的Text屬性來講,相當於一份ViewState中的值發送了兩份客戶端拷貝;
  4. 第二次回發訪問,Label控件初始化,設置Text屬性的值,即向ViewState中添加了一個條目;
  5. 由於是回發訪問,需經歷LoadViewState過程,本例中即讀取ViewState中Text屬性在上一次訪問中的狀態值,而這個值實際上等於過程1中設置的值,讀取的值再次設置Text屬性,
  6. 第二次發送,重複過程2,3.

從過程1控件初始化,到過程6,第二次發送前SaveViewState,在這兩個過程中間,如果不需改變Text屬性的初始值,那麼實際上就不需要使用ViewState。假設我們在過程1、2中間改變Text屬性值,如在Page_Load中如此:

 protected void Page_Loadt(object sender, EventArgs e)
 {
  if (!IsPostBack)
  {
   Label1.Text = "Change Label's value";
  }
 }

 那麼,儘管在回發時不能執行Label1.Text = "Change Label's value";語句,但由於ViewState的作用,第一次訪問設置的值,在第二次回發訪問後仍然會存在,即Label的Text屬性值爲”Change Label's value“,而不是其初始值“Label”。這種情況下才是ViewState的用武之地。注意!IsPostBack的使用,否則你只是每次訪問都進行賦值而已,並沒有利用ViewState的好處。

由此,可以得出,在滿足前面所述的兩個條件時才應該使用ViewState。那麼在現實應用中同時滿足以上兩個條件的情況下多麼,也就是說我們需要使用ViewSate的時候多麼?

很顯然,滿足這兩個條件最大宗的情況就是數據綁定。而我認爲ViewState的設計目的主要就是爲了將必要的信息持久化在頁面中,避免在兩次訪問中(確切的說不只兩次,而是所有的回發訪問中)都要進行數據綁定(而每次數據綁定往往意味着一次次的數據庫訪問)。例如用GridView綁定DataSource控件展現一個類表數據,在ViewSate Enable的情況下,頁面第一次加載時進行數據綁定,在隨後的回發訪問中,如果仍是訪問當前數據視圖,即沒有進行分頁、排序操作等,DataSource不會再進行數據綁定,因爲所有的信息都可以從ViewSate中獲取,不需要再次訪問數據庫再次綁定數據控件了。而如果你將ViewState Disable掉,那麼每次訪問則都需要進行數據綁定了(可以通過SqlProfiler來捕捉SqlDataSource在兩種情況下對數據庫的訪問情況)。這個場景可能最能說明ViewSate的設計初衷了。

然而在實際的應用中,上面的這種場景多麼?在數據列表頁面,往往沒有除了分頁排序等之外的回發操作(你放Grid的頁面裏有回發的按鈕麼?),而分頁,排序操作所引起的回發顯然是需要數據再綁定的。如果是這種場景,那麼你就應該把這個頁面或者把這個Grid的EnableView屬性設爲false了。這裏講點題外話,有人會說如果設成false,那Grid的分頁信息、排序信息怎麼傳遞給後續的回發訪問呢?其實在ASP.NET 2.0中控件的狀態管理被分爲了兩部分view state和control state。兩者的區別是什麼呢,那ASP.NET 1.x中的DataGrid控件來說,DataGrid的所有狀態信息都保存在view state當中,但是這些信息所符合的view state應用場景是矛盾的,比如你的頁面沒有回發操作,你不必把所有數據緩存到view state裏,這時你會把datagrid的enableviewstate屬性設爲false,但當你這麼做後,datagrid的另一些功能如翻頁、排序,就沒法使用了,因爲翻頁排序的狀態信息也是保存在view state中的,如pageindex、sort asc/desc等。就類似於這種問題,ASP.NET 2.0中又引入了control state,control state的存儲方式與view state相同,不同的地方在於它不會被disable掉。這樣control state用來存儲那些控件的功能性的,必需的信息。比如即使GridView的view state被禁止了,它的分頁,排序等信息還是仍然正常工作的。

ASP.NET的一些設計,如整個頁面一個form元素,以及本文談到的view state的使用等,只是在宏觀上,概率統計上能達到節約成本,提高效率的目的。但是具體到一個Web程序, 一個應用場景,一個頁面,甚至是一個控件,如果你知到它們背後的東西,你就會更好更正確的去實現它或使用它。

 

 

 

 

 

發佈了22 篇原創文章 · 獲贊 2 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章