ASP.NET 2.0 的內部變化

簡介

對 ASP.NET 的專業開發人員而言,有關 ASP.NET 2.0 的最大問題就是涉及其內部變化的問題。新功能非常有趣,也令人樂於學習,但 ASP.NET 核心結構的更改對於想真正掌握該技術的開發人員更有吸引力。在此白皮書中,我們將探討自版本 1.x 以來 ASP.NET 2.0 的內部結構是如何變化的。

本文探討的主題對注重性能的開發人員和技術架構師尋求優化應用程序來說非常有用。具體來說,我們將研究代碼模型、編譯、頁面生命週期、可擴展性、性能和緩存的關鍵區域。

本文中的許多示例都要求您相當熟悉 ASP.NET、Visual Basic .NET 和/或 C# 語法。在適當位置,我們就特定主題而進行的深入討論提供參考文檔。

代碼模型

ASP.NET 2.0 內部工作方式最明顯的變化可能與如何創建 ASP.NET Web 頁有關。在本節中,我們將研究對代碼隱藏模型的更改以及這些更改如何影響 ASP.NET 開發。

ASP.NET 1.x中的編碼模型

在 ASP.NET 1.x 中,開發人員開發 Web 窗體主要有兩種選擇。第一,開發人員可以採取傳統的 ASP 模型並直接在 ASPX 頁中編寫代碼。這個過程稱爲代碼內聯,對於簡單的命令非常奏效。但是,對於較複雜的代碼,編寫代碼內聯將導致難以閱讀呈現 (HTML) 與功能(代碼)混合的 Web 頁。

在 ASP.NET 中,改變了這種默認的編碼方法有助於解決這個問題。可以將業務邏輯和事件處理代碼寫入一個只有代碼的文件中,該文件稱爲代碼隱藏 文件。代碼隱藏模型將一個只有代碼的文件與包含呈現標記的 ASPX 文件連接起來。通過將代碼和呈現分離,允許設計人員處理呈現文件而讓開發人員處理代碼文件,則開發小組的工作速度可以更快。


1. ASP.NET 1.x編碼模型

使用代碼隱藏模型的主要困難與代碼隱藏文件必須與 ASPX 頁面同步的方式有關。雖然從編程的意義上來說 ASPX 頁繼承了代碼隱藏文件,但實際上兩個文件通過一種更爲複雜的關係連接在一起。

有關 ASP.NET 1.x 中代碼隱藏模型的更多詳細信息,請參閱 MSDN 技術資源庫文章,Web Forms Code Model

繼承的複雜性

ASP.NET 的設計範例是,開發人員將使用 Microsoft Visual Studio .NET 將控件拖放到 ASPX 頁面。然後,Visual Studio 將自動在代碼隱藏文件中生成適當的支持代碼。如果將控件添加到 ASPX 頁,就必須將新代碼添加到代碼隱藏文件。換句話說,儘管這種繼承關係強調其他的方式,但實際上是 ASPX 頁驅動代碼隱藏文件的設計。

編譯的複雜性

第二個同步問題是文件的編譯方式。所有的代碼隱藏文件,連同支持類,都編譯到一個程序集並存儲在 Web 應用程序的 /bin 目錄中。在部署應用程序之前進行編譯步驟。另一方面,ASPX 頁在第一次請求該頁的運行時進行編譯。ASP.NET 運行庫實際上將 ASPX 頁編譯到它自己的臨時程序集中。

這個過程的問題是,在沒有更新代碼隱藏程序集的情況下可以更改 ASPX 頁。也就是說,部署之後開發人員可能選擇修改 ASPX 頁的一個屬性或者更改控件的類型,但既沒有更新代碼隱藏文件,也沒有重新編譯應用程序程序集。當發生這種情況時,因爲代碼隱藏文件和它們相關的 ASPX 頁不匹配,所以應用程序將會遇到不可預料的錯誤。

ASP.NET 2.0 中的編碼模型

ASP.NET 2.0 繼續提供代碼內聯和代碼隱藏編碼模型。就代碼內聯模型而言,除了 Microsoft Visual Studio 支持單文件開發之外,幾乎沒有變化。有關 Visual Studio 中的變化以及它如何處理代碼內聯的詳細信息,請參閱本文

ASP.NET 2.0 通過修改代碼隱藏文件的特性,解決了代碼隱藏模型的繼承和編譯問題。在 ASP.NET 2.0 中,代碼隱藏文件不再是 System.Web.UI.Page 類的完整實現。取而代之的是,代碼隱藏文件是一種稱爲局部類 的新結構。這種局部類包含所有用戶定義的代碼,但是省略由 Visual Studio .NET 在 ASP.NET 1.x 中自動生成的所有基礎結構和連接代碼。當請求一個具有新代碼隱藏文件的 ASPX 頁面時,ASP.NET 2.0 運行時會真正地將 ASPX 頁和局部類合併爲一個類,而不是兩個單獨的類。


2. ASP.NET 2.0 中的代碼隱藏模型

局部類利用一個新關鍵字(在 Visual Basic 中爲Expands,在 C# 中爲 Partial)來表明該類中的代碼應當在運行時與另一個類合併。同樣,ASPX 頁利用一個稱爲 compilewith 的新指令來表明它與代碼隱藏文件的結合。

比較代碼隱藏文件

如果熟悉傳統的 ASP.NET 1.x 代碼隱藏文件,那麼您一定知道 Visual Studio 可以插入自動生成的控件聲明和初始化代碼。這些自動生成的代碼是代碼隱藏文件和 ASPX 文件之間雙向同步的直接結果。具有一個標籤的典型 ASPX 頁有一個對應的代碼隱藏文件,該文件由許多行自動生成的文本組成。

清單 1. ASP.NET 1.x中的代碼隱藏文件

namespace WebApplication1 

public class WebForm1 : System.Web.UI.Page 

    
protected System.Web.UI.WebControls.Label Label1; 
  
private void Page_Load(object sender,  
    System.EventArgs e) 
{    } 
  
Web Form Designer generated code 
}
 
}
 

自動生成的代碼不但定義標籤(粗體行),它還聲明一個新事件(加載頁)並自動將該事件與自動生成的方法包裝 (Page_Load()) 相連接。

相比之下,ASP.NET 2.0 中同樣的 ASP.NET 頁生成的代碼隱藏文件更爲整潔。

清單 2. ASP.NET 2.0 中的代碼隱藏文件

namespace ASP
public partial class MyPage : System.Web.UI.Page 

}
 
}
 

開發人員可以自動訪問 Label1 並根據需要添加事件。例如,可以添加一個 Page_Load 事件來初始化標籤。

清單 3. 在新代碼隱藏文件中添加事件

namespace ASP
public partial class Webform1_ASPX: System.Web.UI.Page 

void Page_Load(object sender, EventArgs e) 

  Label1.Text 
= "Hello ASP.NET.0"
}
 
}
 
}
 

事件語法可以通過 Visual Studio 生成。獲得的代碼隱藏文件更爲簡短並且不受自動生成代碼的影響。ASP.NET 運行庫自動將代碼隱藏中的事件連接到 ASPX 中的控件。換句話說,ASP.NET 運行庫現在自動執行代碼生成,而過去這由 Visual Studio 完成。

兩步編譯新方法的好處在於,它允許您將代碼隱藏類編譯成二進制,然後支持部署 .ASPX 文件,該文件作爲以後修改的 html 源。您不能部署已編譯的代碼隱藏,以及稍後修改 .ASPX html 源,因爲 .ASPX 源是編譯代碼隱藏所用的局部類型。

繼承的複雜性

這種新代碼隱藏模型大大降低了繼承的複雜性。因爲 ASPX 頁不直接繼承代碼隱藏文件,所以代碼隱藏文件不再需要定義和支持 ASPX 頁上定義的所有控件。同樣,代碼隱藏文件可以自動訪問 ASPX 頁上的任何控件,而不需要 ASP.NET 1.x 中所需的聲明代碼。所有這些都是可能的,因爲 ASP.NET 運行庫自動將所需要的聲明和事件連接代碼插入到最終的已編譯文件中。因爲運行時承擔這樣的責任,所以代碼開發人員和 Web 開發人員都不需要爲此擔心。

在設計期間,鏈接由 Visual Studio 維護。Visual Studio 環境利用 ASP.NET 運行庫編譯塊來確保代碼開發人員和 Web 開發人員可以同步工作。

編譯的複雜性

因爲這種新的代碼隱藏文件與 ASPX 頁連接並在運行時編譯成一個完整類,所以不會出現編譯的複雜性。也就是說,代碼隱藏文件自動與 ASPX 頁同步。即使是使用這種新編譯模型,仍可能有不同步的代碼,但可以很快找到這種問題,因爲產生的異常更爲清楚。

編譯

由於 ASP.NET 1.x 中引入了頁模型,ASP.NET Web 頁的編譯過程總是分成兩個階段。首先,代碼隱藏文件和其他支持類編譯到一個程序集中,然後在運行時編譯單獨的 ASPX 文件。雖然這種模型具有許多優點,但它也有一些缺點。ASP.NET 2.0 提供基本模型的幾種替代模型,並根據您的特定需要提供更爲廣泛的編譯選項。

ASP.NET 1.x中的編譯

ASP.NET1.x 中的主要編譯模型會導致一個應用程序程序集(包含所有的已編譯代碼隱藏文件和其他的源代碼)和一個爲每個被請求的 ASPX 頁而創建的臨時程序集。在有些情況下,編譯器優化(例如批處理)會引起將臨時 ASPX 頁編譯到同一個程序集中。在任一種情況下,每個 ASPX 頁都編譯到一個臨時程序集中,這樣它可以加載到 ASP.NET 運行庫。


3. ASP.NET 1.x中的編譯

雖然這種模型有優點,但它也有兩個主要缺點。首先,ASPX 頁必須要以人們可以閱讀的形式部署到 Web 站點。如果開發人員使用代碼內聯 模型,這意味着,一些(或所有)的業務邏輯也可以部署在生產服務器上。雖然沒有將 IIS 和 ASP.NET 配置爲公開原始的 ASPX 頁,但聰明的攻擊者仍可以通過任何攻擊(該攻擊打開到 Web 服務器的通道)來訪問這些文件。其次,第一次有人請求某 Web 頁時,響應速度將比正常速度慢一些,原因在於 ASP.NET 運行庫必須編譯 ASPX 頁。

這整個過程中,開發人員擁有的唯一控制權是決定是否批編譯 ASPX 頁。在 ASP.NET 1.x 中,可以通過修改 <compilation> 標記在 web.config 文件中配置批編譯。

清單 4. 配置批編譯

<compilation  
  
batch="true|false"
  batchTimeout
="number of seconds"            
  maxBatchSize
="maximum number of pages per batched compilation"
  maxBatchGeneratedFileSize
="maximum combined size (in KB) of the 
   generated source file per batched compilation"
          
</compilation
>

批編譯用啓動時間換取減少第一次請求 Web 頁的加載時間。批編譯的另一個好處是所有的 ASPX 文件都編譯到一個臨時程序集中,而不是一頁一個臨時程序集。

ASP.NET 2.0 中的編譯

ASP.NET 2.0 爲 Web 應用程序提供三種不同的編譯模型:

普通 (ASP.NET 1.x) — 在一個普通的 ASP.NET Web 應用程序中,代碼隱藏文件被編譯到一個程序集並存儲在 /bin 目錄中。根據要求編譯 Web 頁 (ASPX)。該模型對大多數 Web 站點都運行得不錯。但是,編譯過程使得第一次請求 ASP.NET 頁時的速度比隨後的請求速度緩慢。ASP.NET 2.0 繼續支持這種編譯模型。

部署預編譯 — ASP.NET 2.0 的一種新功能,允許在部署前對項目進行完整編譯。在完整編譯中,所有的代碼隱藏文件、ASPX 頁面、HTML、圖形資源以及其他的後端代碼都被編譯到一個或多個可執行程序集中,這取決於應用程序的大小和編譯設置。這些程序集包含所有的已編譯 Web 站點代碼,而資源文件和配置文件被複制,沒有做修改。這種編譯方法以犧牲修改部署後 Web 站點的能力爲代價,提供了最好的性能和安全性。如果您使用高可見或高安全的 Web 站點,這種選項是最終部署的最好選擇。但是,如果您正在構建一個運行局部 Intranet 的小站點,並且更改站點非常頻繁,那麼完整預編譯可能有點過分。

ASP.NET 2.0 編譯模型也允許預編譯應用程序的所有代碼隱藏文件並且仍可以更新代碼。可以將代碼隱藏文件和原始的 .ASPX 文件(都是局部類)編譯到一個預編譯類中(頁面的基類)。如果選擇在運行時編輯 .ASPX 文件,只需重新編譯頁面即可。

完整的運行時編譯 — 在部署預編譯的另一個極端,ASP.NET 2.0 提供一種在運行時編譯整個應用程序的新機制。也就是說,可以將未編譯的代碼隱藏文件和其他相關的代碼放在 /app_code 目錄中,並讓 ASP.NET 2.0 創建並維護對程序集的引用,這些引用將在運行時根據這些文件生成。這種選項以在服務器上存儲未編譯代碼爲代價,在更改 Web 站點內容方面提供了最大的靈活性。

選擇最佳的編譯選項要由具體的情況和需要決定,但編譯模型要有靈活性。即使選擇使用 /app_code 目錄來存儲代碼隱藏文件,您仍可以使用完整的編譯方法來部署應用程序。

批編譯

在 ASP.NET 2.0 中,可以利用單個 URL 請求來批編譯任何應用程序。如同 ASP.NET 1.x 一樣,批編譯消除了第一次頁面請求的延時,但造成了更長的啓動週期。另外,批編譯還要求在部署前編譯代碼隱藏文件。

Web.config 批編譯設置在 ASP.NET 2.0 中仍起作用。批編譯的優點是,第一個用戶可以立即使用頁面,而且在批編譯期間可以檢測到 ASPX 頁中的任何錯誤。但是,批編譯的確增加了應用程序啓動的延時,並且必須要內置在 Web.config 文件中。應當注意,如果某個文件出現了問題,則該批將不會接收它。

部署預編譯

部署預編譯允許創建一個或多個程序集,這些程序集是 Web 站點的可執行版本。所獲得的程序集包含 Web 站點的已編譯代碼。HTML 頁面、資源、配置文件和 ASPX 頁面被單獨複製。

部署預編譯要求使用一個稱爲 ASPnet_compiler.exe 的命令行實用程序。該實用程序創建一個目標部署目錄,該目錄包含一個含有程序集的 /bin 目錄和各種 ASPX 頁的 stub 文件。該實用程序還用來在原地進行預編譯,類似於調用“魔術頁”的行爲。stub 文件共享 ASPX 頁的名稱,但是包含調用已編譯程序集的簡單代碼。換句話說,ASPX 頁只是空殼而不是填滿的功能頁。

通過爲部署預編譯 Web 站點,您可以獲得增強的安全性,因爲只有進行反編譯程序集才能訪問您的代碼。爲了增強保護,可以弄亂所得到的程序集,使您的 Web 應用程序更加安全。部署預編譯的主要缺點是,在部署前必須執行這些步驟,並且在部署後不能更改 Web 站點。如果想進行更改,就必須重新編譯該 Web 站點並重新部署它。

對於大多數主要的 Web 應用程序,部署預編譯選項將是部署的首選機制,因爲它減少了在 Web 服務器上部署的原始代碼數量,並提供了最佳的安全性。這個增加的進程可以內置於通常的開發/測試/部署週期中,而工作效率並不會有多大損失。

完整的運行時編譯(/app_code 目錄)

在目前描述的所有三種編譯方法中,在部署前必須要編譯所有的代碼文件(代碼隱藏類和支持類)。在 ASP.NET 2.0 中,您有代碼目錄。

/app_code 目錄是一個保存未編譯類的特殊目錄。在運行時,ASP.NET 運行庫將該目錄中的內容編譯到一個程序集中,應用程序中的 ASPX 頁自動引用該程序集。換句話說,通過使用代碼目錄,可以避免爲支持代碼創建和引用單獨的程序集。代碼目錄的優點在於,不用完整編譯項目就可以部署,因此減少了不匹配的可能。缺點是,有可能在服務器上公開未編譯的代碼。

該選項最適合於不需要大量支持代碼(以代碼隱藏文件的形式或外部對象的形式)的 ASP.NET 應用程序。對於一個簡單的應用程序,與更爲健壯的編譯方法相比,快速部署和測試系統的功能提供了幾個優點。

頁面生命週期

ASP.NET 2.0 在 ASP.NET 頁的生命週期中提供了兩個主要變化。第一,ASP.NET 2.0 公開新的事件來支持新功能,包括母版頁、個性化以及集成的移動設備支持。第二,ASP.NET 2.0 爲 Web 窗體引入跨頁投遞。

新事件

與 ASP.NET 1.x 相比,ASP.NET 2.0 提供了一種粒度更細的頁面生命週期方法棧。這些添加的方法爲 Web 開發人員提供了更高級的控制。可以通過 ASP.NET 頁上的 Page 對象來訪問這些事件。

表 1 顯示綜合方法列表。Method 列顯示實際事件方法名稱,Active 列表示該事件是否總處於活動狀態或者僅在 PostBack 操作期間是活動的。例如,新方法 TestDeviceFilter 可用來確定哪種設備篩選器就緒,並用這些信息來決定如何顯示頁面。另一方面,新方法 LoadControlState 只有在 PostBack 期間才激發。可以重寫該方法(結合 SaveControlState)來在 PostBack 期間創建保存和還原控件狀態的備選序列化方案。

表 1. 頁面生命週期方法
方法 活動

Constructor

Always

Construct

Always

TestDeviceFilter

Always

AddParsedSubObject

Always

DeterminePostBackMode

Always

OnPreInit

Always

LoadPersonalizationData

Always

InitializeThemes

Always

OnInit

Always

ApplyControlSkin

Always

ApplyPersonalization

Always

OnInitComplete

Always

LoadPageStateFromPersistenceMedium

PostBack

LoadControlState

PostBack

LoadViewState

PostBack

ProcessPostData1

PostBack

OnPreLoad

Always

OnLoad

Always

ProcessPostData2

PostBack

RaiseChangedEvents

PostBack

RaisePostBackEvent

PostBack

OnLoadComplete

Always

OnPreRender

Always

OnPreRenderComplete

Always

SavePersonalizationData

Always

SaveControlState

Always

SaveViewState

Always

SavePageStateToPersistenceMedium

Always

Render

Always

OnUnload

Always

查看頁面生命週期的底層細節,我們可以看到 ASP.NET 2.0 中提供的許多功能(例如主題和個性化)將在什麼地方容易實現。例如,主題在 IntializeThemes 事件中處理,而個性化數據將在 LoadPersonalizationData 中加載並稍後用於 ApplyPersonalization 方法。請注意,就哪一個 UI 元素將決定 Web 應用程序的最終外觀和感覺而言,方法的順序非常重要。

跨頁投遞

頁面生命週期中的另一個主要變化涉及回發事件和 Web 窗體。在 ASP.NET 1.x 中,Web 窗體自動回發給它們的宿主頁。即,當用戶提交一個窗體時,窗體數據總是提交回包含該原始窗體的頁面。這種設計決策考慮了存儲控件狀態的容易性,但限制了開發人員執行更復雜操作的能力。

在 ASP.NET 2.0 中,Web 窗體控件有一個新特性,讓開發人員決定在進行提交操作時將窗體數據發送到何處。在大多數情況下,要求 PostBack 機制,因此它仍是默認的機制。但是,如果開發人員想將數據投遞到不同的窗體,現在是可能的。


4. PostBack 與跨頁投遞

例如,可以創建一個包含幾個不同窗體的多頁嚮導。每個窗體依次提交給下個頁面,直到用戶到達一個進行最後驗證的摘要頁。通過 PreviousPage 對象可以在當前的上下文中訪問來自最後一頁的數據。PreviousPage 對象存儲來自前頁的已驗證數據,以在當前頁中使用。由於這個對象,跨頁投遞不會犧牲控件的持久性存儲。如果用戶需要依次倒退回一個窗體,那麼就可以立即訪問該頁的數據,而用戶不必重新輸入所有的數據。

可擴展性

ASP.NET 最初被設計爲一種開放式框架。即,可以擴展、修改或替換構成 ASP.NET 的許多模塊和組件,以適應特定的要求。在 ASP.NET 2.0 中,這種框架的可擴展特性由新的 HTTPHandlersHTTPModules 清楚地闡明,二者現在是該框架的一個標準部分。

請求管道

在 ASP.NET 中,請求從 Web 服務器通過 Internet 服務器應用程序編程接口 (ISAPI) 篩選器傳遞,並繼續傳遞給實際的 ASP.NET 運行庫。


5. 請求管道

當 IIS 接收一個請求時,根據 IIS 的設置將擴展映射到一個 ISAPI 篩選器。將 .ASPX、.asmx、.asd 和其他擴展映射到 ASPnet_isapi.dll,該 ASPnet_isapi.dll 只是一種啓動 ASP.NET 運行庫的 ISAPI 篩選器。一旦請求到達 ASP.NET 運行庫,它在 HTTPApplication 對象處啓動,該對象擔當 ASP.NET Web 應用程序的宿主。HTTPApplication 對象:

1.

讀取機器級和應用程序級的配置文件。

2.

通過一個或多個 HTTPModule 實例傳遞請求。每個 HTTPModule 提供一種服務,例如會話維護、身份驗證,或配置文件維護。這些模塊將請求傳遞迴 HTTPApplication

3.

根據謂詞和路徑將請求傳遞給 HTTPHandler。謂詞指請求中使用的 HTTP 謂詞(GET、POST、FTP,等等),而路徑指應用程序中的 URL。根據處理程序的配置方式,該請求可能作爲一個 ASP.NET 頁(System.Web.UI.PageIHTTPHandler 的一種實現)加以處理,或者該請求可能觸發另一個操作,例如批編譯所有的 Web 頁(precomiplation.asd 觸發 PrecompHandler)。

在 ASP.NET 2.0 中,該模型沒有變化,但是,添加了幾種新模塊和處理程序以提供其他的服務。與 ASP.NET 1.x 一樣,您可以擴展、替換或重新配置任何模塊或處理程序類,以提供自己的自定義功能。

新模塊

顯然,已經添加了新 HTTPModules 以支持 ASP.NET 2.0 中提供的新服務。具體地說,具有默認模塊設置的 ASP.NET 應用程序將包括爲以下目的而添加的新模塊:

SessionID — 會話識別機制已經從 ASP.NET 1.x 會話模塊分離,以提供對 cookie、URL 重寫以及會話 ID 生成的其他形式的更多控制。

角色管理 — 添加的這種新模塊用於提供基於角色的服務,以支持新用戶識別機制。該模塊有助於將 ASP.NET 應用程序和內置在 .NET 框架中基於角色的安全性結合起來。

匿名識別 — 新的個性化功能支持匿名用戶。該模塊有助於跟蹤匿名用戶可以訪問的功能,以及跟蹤在請求之間維護這些功能的方式。

配置文件 — 該配置文件模塊連接新的配置文件服務,幫助爲用戶提供特定的持久數據存儲。

除了這些新模塊,一些舊模塊的行爲也進行了更改:例如,輸出緩存模塊現在支持新的緩存技術,這將在本白皮書的稍後部分說明。

新處理程序

除了這些新模塊,ASP.NET 2.0 還引進了新的處理程序來支持應用程序配置工具以及其他的新功能,例如,批編譯請求。這些新處理程序中最重要的一點是包括處理 Web 站點管理請求的“.axd”系列。這些處理程序啓動內部的管理工具,這些管理工具允許開發人員配置 ASP.NET 用戶和其他設置。管理處理程序包括:

Web 管理 — WebAdminHandler 是管理 Web 站點的主頁。該處理程序爲管理 ASP.NET 2.0 Web 應用程序提供了起點。

跟蹤 — ASP.NET 1.xTraceHandler 已進行了改進,它是 ASP.NET 1.x 中唯一的“axd”處理程序。

Web 資源 — 由於有了新的管理工具和 WebResourcesHandler,現在可以在部署後配置 Web 資源。

緩存圖像 — CachedImageServiceHandler 支持緩存圖形組件。

計數器 — SiteCountersHandler 使用頁面計數器模塊爲 ASP.NET 2.0 應用程序提供訪問統計信息。

預編譯 — 正如先前提到的一樣,可以使用 PrecompHandler 在一個 ASP.NET 應用程序中批編譯所有的 ASPX 頁。

Web 部件導出 — WebPartExportHandler 支持存儲和傳輸 Web 部件佈局。Web 部件是一種新機制,用於個性化門戶樣式的 Web 應用程序的外觀和內容。

與以前一樣,HTTPForbiddenHandler 連接到不應該返回的任何文件類型。在 ASP.NET 2.0 中,禁止的文件類型列表已擴展爲包括母版頁、外觀文件和其他的開發人員新組件。

高級緩存技術

提高 Web 應用程序性能的一種方法是在內存中緩存靜態內容。緩存內容的返回速度始終比新提供內容的速度快。但是,換來的是緩存的內容可能會過時。ASP.NET 1.x 支持幾種緩存,包括:

頁面級別 — 每頁可以作爲一個整體塊或根據用來訪問該頁的參數進行緩存。緩存頁在一段固定時間之後過期。

頁面片段 — 如果使用用戶控件(.ascx 文件)來構建頁面,那麼可以將用戶控件獨立於其他頁面內容進行緩存。

編程緩存 — 由於有了緩存 API,開發人員還可以緩存對象。緩存 API 具有獨特的優勢,提供一種在應該刷新緩存時創建不同類型依賴項的方式。

在 ASP.NET 2.0 中,頁面級別的緩存機制已經擴展到了支持數據庫依賴項。利用數據庫緩存依賴項,緩存頁可綁定到 SQL Server 數據庫的一個特定表。當該表更改時,緩存自動過期。另外,開發人員現在可以使用緩存後替換來用刷新內容替換部分緩存內容。緩存後替換允許應用程序使用頁面級別緩存,即使部分頁面應當動態生成。

數據庫緩存無效

對於大多數數據驅動的 Web 站點,緩存是一個麻煩的問題,特別是在要求緩存以及需要更新數據時。在 ASP.NET 1.x 中,可通過輸入參數(查詢字符串或 POST 參數)將頁面緩存一定長度的時間和組織頁面:

清單 5. ASP.NET 1.x輸出緩存指令

<%@ outputcache duration="3600" varybyparam="ProdID" %> 

例如,清單 5 中的代碼根據變量 ProdID 將頁面在內存中緩存一個小時。上例中出現的問題是,如果在別處更新了相關的業務數據,應當如何處理。例如,考慮一個按產品 ID 緩存的產品目錄頁。如果在一個管理站點更新有關該產品的信息(例如,供應的數量或價格),就會將錯誤的數據緩存並顯示給客戶。對於以前版本的 ASP.NET,該問題的解決方案要求利用 Response.RemoveOutputCacheItem 手工地將該頁從緩存中刪除,或者等到 duration 時間到期並允許系統自動更新該頁。

ASP.NET 2.0 通過支持數據庫緩存依賴項解決了這個問題。當使用 SQL Server 7 和 2000 時可用表級通知,而 Microsoft SQL Server 將以更多的粒度級別來提供通知。例如,下列代碼緩存一個產品頁最多 1 個小時,但添加了第二個數據庫表依賴項。

清單 6. ASP.NET 2.0 數據庫緩存示例

<%@ outputcache duration="3600" varybyparam="ProdID" sqldependency="MyDatabase:Products" %>  

利用 sqldependency 新屬性,如果對 Products 表有任何更改,那麼緩存頁將到期。sqldependency 屬性必須引用一個 datasource,它在 web.config 文件中進行配置。datasource 識別數據庫連接和必需的參數,以使依賴項通知工作。

自定義緩存依賴項

ASP.NET 2.0 配備一個 CacheDependency 實現,即 SQLCacheDependency 類,它支持 Microsoft SQL Server。實現一個新的緩存依賴項將是一個複雜的過程,但是由於有了 ASP.NET 2.0 的可擴展特性,這種實現是可能的。換句話說,可以創建自己的 CacheDependency 類來爲數據庫系統(例如 Oracle 或 Sybase)提供類似的功能。

緩存後替代

一些頁面元素保持動態而大多數頁將從緩存中受益,對於這種情況,ASP.NET 2.0 提供一種稱爲緩存後替代的功能。緩存後替代用來通知 ASP.NET 運行庫緩存頁呈現給用戶之前應該重新評估該頁上的某個特定元素。

使用這種功能有兩種方法:

調用新方法 Response.writeSubstitution,將一個引用傳遞給替代回調函數。

給 Web 頁添加一個<asp:substitution> 控件並將 methodname 屬性設置爲回調函數的名稱。

對於任一種選項,應當給頁面添加一個 @OutputCache 指令,指定依賴項的持續時間和位置。

實現緩存後替代

爲了利用這種功能,可以創建能夠識別緩存後替代的控件。AdRotator 控件就是一個這樣的控件示例。清單 7 舉例說明了這樣一個頁:

檢索 Pubs 數據庫的作者表數據。

將數據綁定到 GridView 控件。

從一個 AdRotator 顯示地址。

在一個標籤控件中顯示創建該頁的時間。

該示例中還添加了一個 <asp:substitution>控件(清單中的粗體行)。該控件的 methodname 屬性設置爲 uncachedUpdate(一種返回字符串輸出的方法 — 在這種情況下爲當前時間)。無論緩存什麼內容,替代控件都將返回正確的時間。

清單 7. PostCache.ASPX 源代碼

<%@ Page language="C#" Codefile="PostCache.ASPX.cs"  
AutoEventWireup
="true" Inherits="WebApplication1.PostCache" 
%> 
<%@ outputcache duration="30" varybyparam="none" %> 
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" > 
<HTML> 
<HEAD> 
  
<title>WebForm1</title> 
</HEAD> 
<body MS_POSITIONING="GridLayout"> 
  
<form id="Form1" method="post" runat="server"> 
    
<DIV style="DISPLAY: inline; 
      Z-INDEX: 101; LEFT: 32px; WIDTH: 160px; 
      POSITION: absolute; TOP: 24px; HEIGHT: 8px"
 
      align
="right" ms_positioning="FlowLayout"> 
        this page was created at: 
    
</DIV> 
    
<ASP:Label id="CreatedTime" 
      style
="Z-INDEX: 102; LEFT: 200px; POSITION: absolute; 
      TOP: 24px"
 runat="server" Width="120px" Height="16px"> 
    
</ASP:Label> 
    
<ASP:substitution id="UpdatedTime" methodname="uncachedUpdate" 
      style
="Z-INDEX: 103; LEFT: 200px; POSITION: absolute; 
      TOP: 48px"
 runat="server" Width="112px" Height="11px"> 
    
</ASP:substitution> 
    
<DIV style="DISPLAY: inline; Z-INDEX: 104; LEFT: 32px; 
      WIDTH: 160px; POSITION: absolute; TOP: 48px; 
      HEIGHT: 16px"
 align="right" ms_positioning="FlowLayout"> 
        and last updated at: 
    
</DIV> 
    
<ASP:AdRotator id="Ads" style="Z-INDEX: 105; LEFT: 312px; 
      POSITION: absolute; TOP: 16px"
 runat="server" 
      Width
="80px" Height="60px" AdvertisementFile="img/Ads.xml"> 
    
</ASP:AdRotator> 
  
</form> 
</body> 
</HTML> 

該頁的代碼隱藏文件包含支持 uncachedUpdate 方法的緩存後替代所必需的事件。請注意,Page_Load 方法報告加載該頁的時間,因此我們可以確定緩存發生的時間。

清單 8. PostCache.ASPX.cs

using System; 
using System.Collections; 
using System.ComponentModel; 
using System.Data; 
using System.Drawing; 
using System.Web; 
using System.Web.SessionState; 
using System.Web.UI; 
using System.Web.UI.WebControls; 
using System.Web.UI.HtmlControls; 
namespace WebApplication1 
public class PostCache : System.Web.UI.Page  
  
protected System.Web.UI.WebControls.Label CreatedTime; 
  
protected System.Web.UI.WebControls.Label UpdatedTime; 
  
protected System.Web.UI.WebControls.AdRotator Ads; 
  
private void InitializeComponent()  
    
this.Load += new System.EventHandler(this.Page_Load); 
  }
 
  
private void Page_Load(object sender, System.EventArgs e) 
    CreatedTime.Text 
= DateTime.Now.ToShortTimeString();   
  }
 
  
protected String uncachedUpdate()  
    
return DateTime.Now.ToShortTimeString(); 
  }
 
}
 
}
 

當前使用的緩存後替代

6 顯示 PostCache 頁的輸出結果。首次運行應用程序,我們可以看到“創建頁”時間和“最後更新”時間是相同的。


6. PostCache.ASPX 的輸出結果

連續調用同一個頁面,我們可以看到緩存後替代的效果。雖然頁面創建時間和圖像保持不變,但最後更新時間有變化。


7. 第二次請求的緩存後輸出結果

由於緩存指令,創建時間和 adRotator 圖像都保持不變。該頁緩存了 30 秒。一旦時間到期,下次請求時創建時間和 adRotator 都將更新。但是,<asp:substitution> 控件(調用 uncachedUpdate() 方法)在每次請求頁面時都將更新,而不管其緩存狀態。

通過正確操作緩存後替代,開發人員可以通過只更新其頁面的動態內容以顯著提高其 Web 應用程序性能。結合數據庫緩存無效和異步頁更新,用 ASP.NET 2.0 開發的 Web 應用程序將消除由 Web 的傳統請求和響應體系結構所施加的許多限制。

性能

雖然在 ASP.NET 2.0 中更改了基礎結構並增加了功能,但還有一個問題,ASP.NET 2.0 的執行速度有多快?雖然沒有可用的性能衡量標準(因爲 ASP.NET 2.0 仍在開發過程中),但是已花費了相當多的精力來確保ASP.NET 2.0 框架的各個方面的性能保持穩定或有所提高。

改進的請求管道

每個開發人員都將看到性能得以提高的一個區域是在請求管道中。儘管添加了許多新的事件掛鉤,但是基本的 ASP.NET 請求棧的速度比在 ASP.NET 1.1 中的速度快。通過創建一個顯示“Hello World.”的簡單頁,可以評估提高的性能。因爲該頁沒有高級功能,所以直接測試的是 HTTPHandlerHTTPModule 管道,以及將 ASP.NET 2.0 連接到 IIS 的 ISAPI 插件。無論使用的是哪一種版本的 IIS,應當可以看到性能得到了提高,因爲這些代碼已經針對更快的吞吐量進行了優化。

利用 IIS 6.0 改進的內存管理

一些 ASP.NET 2.0 中的性能改進只有在與 IIS 6.0 結合使用時才能體現。例如,在 IIS 6.0 中,在利用 100 個併發用戶通過幾個控件請求某個頁面的負載測試中,輔助進程的工作集被降低了大約 50%。這意味着,對於一個給定的服務器,操作系統使用的資源大約是以前所需資源的一半。

在設計的用來模仿中等複雜 ASP.NET 頁的某項測試中,與運行在 IIS 5.0 上的相同頁面相比,系統負載(內存和 CPU 使用率)顯著下降。這種特定的性能改進是通過將響應緩衝區從託管內存移到本機內存完成的。通過消除將託管內存固定到某個特定響應的必要性,ASP.NET 2.0 消除了資源瓶頸並對每個請求生成響應的速度更快。

其他的性能改進利用了 IIS 6.0 與 Windows 操作系統內核的緊密集成。IIS 6.0 在內核級別執行它的一些緩存和緩衝功能,這爲所有的 Web 應用程序(包括 ASP.NET)提高了性能。

其他改進

作爲一名開發人員,您希望 ASP.NET 2.0 的運行速度與 ASP.NET 1.x 一樣快,或者比它還快。既然已經構建了核心功能,那麼在 ASP.NET 2.0 的最後版本中一定能看到期望的其他性能改進。 

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