天天看點

深入剖析ASP.NET 2.0緩沖機制

一、簡介 ASP.NET 1.x Cache API是一種革命性特征。當一個XML檔案或另一個緩沖項的内容改變時,Cache API提供了諸如聲明性輸出緩沖、以程式設計方式控制輸出緩沖以及使緩沖項無效等能力。盡管這大大改進了Web應用程式的性能,但遺憾的是,ASP.NET 1.x并沒有提供一種機制來實作當資料庫中的資料改變時使一個緩存對象中的資料無效。最終,在ASP.NET 2.0中加入了這一特征。此外,ASP.NET 2.0還提供了特定功能用于緩沖一個SqlDataSource控件的輸出—你可以充分利用緩沖優點而不用編寫一行代碼。本文中,讓我們共同讨論這些新的 緩沖特征,以及如何把它們應用于自己的Web應用程式。 在ASP.NET 2.0中,緩沖以多種方式加以改進。也許最引人矚目的改進是引入了資料庫觸發的緩存無效機制。在ASP.NET 1.x中,你能夠基于一些預定義條件(例如一個XML檔案中的改變,在另一個緩存項中的改變等)來使一個緩沖項無效。借助于這一特征,當資料或另一個緩沖 項改變時,你可以删除或從緩存中使該項無效。然而,當一個SQL Server資料庫中的資料改變時,ASP.NET 1.x Cache API并不允許你使緩存中的一個項成為無效的,盡管大多數應用程式都要求具有這種能力。相比之下,ASP.NET 2.0則特别重視這一點—通過提供資料庫觸發的緩存無效功能,它使你確定緩存中的項與資料庫中的改變保持同步更新。 ASP.NET 2.0另一個重要的緩沖特征是能夠在SqlDataSource級上支援緩沖。該SqlDataSource控件能夠操作SQL Server、OLEDB、ODBC和Oracle資料庫等多種資料庫,而且支援你使用SQL指令選擇、更新、删除和插入資料。現在,借助于在 SqlDataSource控件級上設定緩沖屬性的能力,你可以更為細緻地控制緩沖的資料。 ASP.NET 2.0還提供一個新的Substitution控件,你能夠使用它來把動态的内容注入到另一個緩沖的Web頁面中。如果你有一個啟動了輸出緩沖功能的頁面 但是仍然想顯示動态的内容(這需要在每次請求該頁面時重新生成)的話,那麼你可以考慮使用該Substitution控件。 下列幾節中,我将詳細分析上面的特征并提供相關的示例。 【提示】本文中提供了三個簡單但完整的aspx頁面源碼,我使用Visual Studio 2005在一個簡單web工程Caching中分别試驗,并用浏覽器預覽這三個頁面。至于如何建立工程與SQL Server 2005/2000/7.0示例資料庫Northwind的連接配接,在此略過。 二、在SqlDataSource控件中實作基于時間的緩存無效 ASP.NET緩沖是一種能夠改進Web應用程式性能的重要特征。事實上,改進一個資料庫驅動的Web應用程式性能的最顯著的方法正展現在對緩沖機 制的巧妙把握上。在絕大多數情況下,從資料庫中檢索資料成為你能夠實作的最慢的網站操作之一。然而,如果你能夠在記憶體中恰當地緩存資料庫資料并且盡可能在 每一次頁面請求時避免通路資料庫,那麼你一定能夠顯著地改進你的應用程式的性能。 ASP.NET 2.0提供了比ASP.NET 1.x更強的緩沖特征。其中,一個新的特征是,它能夠把caching屬性指定為資料源控件聲明的一部分。ASP.NET 2.0中的這個新的資料源控件能夠“無縫”地利用ASP.NET 2.0新的緩沖特征特征,進而使你能夠把caching屬性設定為SqlDataSource控件聲明的一部分。 典型地,你可以通過設定SqlDataSource控件中的下列兩個屬性來支援緩沖: ◆EnableCaching—通過把這個屬性設定為true,你可以啟動一個SqlDataSource控件的緩沖功能。 ◆CacheDuration—這個屬性允許你設定或得到SqlDataSource控件中的緩沖資料的持續時間(這個屬性以秒為機關指定)。 作為本文示例,不妨讓我們考慮一下Northwind資料庫中的categories和products表的情況。它在一個 DropDownList中顯示所有的産品種類并在一個GridView控件中顯示屬于一個特定種類的産品。首先,讓我們使用Visual Studio 2005建立一個新的網站,并命名為Caching。然後,把一個Web頁面TimeBasedCaching.aspx添加到其中。最後,按如下所示修 改TimeBasedCaching.aspx檔案中的代碼:

<%@ Page Language="C#" %>

<html>

<head>

<title>SqlDataSource控件緩沖與參數分析試驗</title>

</head>

<body>

<form id="form1" runat="server">

<asp:DropDownList DataValueField="CategoryID"

DataTextField="CategoryName"

DataSourceID="CategoriesDataSource"

ID="DropDownList1" Runat="server" AutoPostBack="True">

</asp:DropDownList>

<br/><br/>

<asp:GridView ID="GridView1" Runat="server"

DataSourceID="ProductsDataSource"

DataKeyNames="ProductID" AutoGenerateColumns="False">

<Columns>

<asp:BoundField HeaderText="ProductID" DataField="ProductID"

SortExpression="ProductID" />

<asp:BoundField HeaderText="Timestamp" DataField="Timestamp"

SortExpression="Timestamp" />

<asp:BoundField HeaderText="ProductName"

DataField="ProductName"

SortExpression="ProductName" />

<asp:BoundField HeaderText="QuantityPerUnit"

DataField="QuantityPerUnit"

SortExpression="QuantityPerUnit" />

<asp:BoundField HeaderText="UnitPrice" DataField="UnitPrice"

SortExpression="UnitPrice" />

</Columns>

</asp:GridView>

<asp:SqlDataSource ID="ProductsDataSource" Runat="server"

SelectCommand="SELECT DatePart(second, GetDate())

As Timestamp, *

FROM [Products] where CategoryID = @CategoryID"

ConnectionString="<%$ ConnectionStrings:Northwind %>"

EnableCaching="True" CacheDuration="10">

<SelectParameters>

<asp:ControlParameter Name="CategoryID"

ControlID="DropDownList1"

PropertyName="SelectedValue" />

</SelectParameters>

</asp:SqlDataSource>

<asp:SqlDataSource ID="CategoriesDataSource" Runat="server"

SelectCommand="SELECT * FROM [Categories]"

ConnectionString="<%$

ConnectionStrings:Northwind %>"

EnableCaching="True" CacheDuration="10"/>

</form>

</body>

</html>

在上面的代碼中,到資料庫的連接配接字元串是從web.config檔案中檢索的。這個web.config檔案包含下列connectionStrings元素:

<connectionStrings>

<add name="Pubs"

connectionString="server=localhost;database=Pubs;

trusted_connection=true"/>

<add name="Northwind"

connectionString="server=localhost;database=Pubs;

trusted_connection=true"/>

</connectionStrings>

現在,既然要求的連接配接字元串已經在web.config檔案中定義,那麼,SqlDataSource控件就能夠借助于下列聲明來使用這個連接配接字元串:

<%$ ConnectionStrings:Northwind %>

上面的代碼檢索定義在Northwind連接配接字元串元素的connectionString屬性中的連接配接字元串值。 SqlDataSource控件還要把EnableCaching屬性設定為true,這樣會使SqlDataSource自動地緩沖通過 SelectCommand檢索的資料。這個CacheDuration屬性能夠使你指定(以秒為機關)在資料從資料庫中重新整理之前應該被緩沖多長時間。默 認情況下,SqlDataSource将使用一種絕對過期政策來緩存資料;這意味着,資料每隔在CacheDuration屬性中指定的秒數重新整理一次。 你還可以選擇性配置讓SqlDataSource使用一種“滑動式”過期政策。通過這種政策,隻要資料繼續被存取,它就不會被删除。當你有大量需要 被緩沖的項時,使用滑動過期政策非常有用,因為該政策能夠使你在記憶體中僅保持被最頻繁通路的項。在上面的示例中,通過分别把EnableCaching和 CacheDuration屬性設定為True和10,你緩沖SQL查詢結果的時間期限為10秒。 三、在SqlDataSource控件中的SQL緩存無效 到目前為止,你已看到了如何使基于在SqlDataSource控件中的CacheDuration屬性中設定的持續時間值緩存無效。在本節中,我 将解釋基于SQL Server表中的資料實作一個緩存無效機制的步驟。這種緩存無效機制是ASP.NET 2.0的一個新特征,這時一個緩沖頁面或一個資料源控件能夠被綁定到一個SQL Server資料庫中的某一個特定表上。一旦你實作這種初始化的關聯操作,那麼,改變該表的内容将導緻緩沖的對象自動無效。 【建議】當你需要在多個頁面中使用相同的資料庫資料時,你應該考慮使用資料源控件的SQL緩存無效機制。 其實,基于SQL Server的緩存無效機制适用于SQL Server 7.0及以上版本;但是,SQL Server 7.0和2000僅支援表級的緩存無效機制。這意味着,當任何時間表中的資料改變時,緩沖的項被自動地無效。SQL Server 2005還提供了一種行級緩存無效機制,這可以在一種更細的級别上來控制緩沖資料。 在SQL Server 7和SQL Server 2000中,表級緩存無效是通過使用一種查詢系統(一個特定的ASP.NET程序)支援的。該程序每隔特定的時間查詢資料庫(“拉”模型)以監視自從上次 被檢查以來哪些表發生了變化。盡管這種拉模型能夠适合于大多數情形,但是它并不是一種非常有效的方法。幸好,這種情形在SQL Server 2005中得到了大大增強—無論何時修改一個特定的資料行,實際上都由SQL Server負責通知(推模型)ASP.NET。SQL Server 2005通過使用一個稱為通知送出服務(Notification Delivery Services,它使用80端口)的特征實作這一功能,該服務能夠直接與IIS6.0的HTTP.SYS進行互動以通知Web伺服器更新特定的行。接下 來,我将解釋如何為SQL Server 7和2000版本配置緩沖。 在你能夠使用SQL Server 7或SQL Server 2000建立緩存依賴性之前,你需要完成下列步驟: ◆配置SQL Server以支援SQL緩存無效。這隻需要一次性建立你想監視的SQL Server資料庫中的表或資料庫即可。 ◆把必要的配置資訊添加到web.config檔案。 下面,讓我們詳細分析上面的步驟。首先,我們來看一下SQL Server的配置問題。 四、配置SQL Server以支援SQL緩存無效 你可以以下列兩種方式實作SQL Server 2000配置以支援SQL緩存無效: 1.使用aspnet_regsql工具; 2.使用SqlCacheDependencyAdmin類的EnableTableForNotifications方法。 在本文中,我們僅考慮第一種方法。上面的aspnet_regsql工具負責建立另外一個表 AspNet_SqlCacheTablesForChangeNotification,用于跟蹤改變資料庫中的所有被監視的表的變化;它還建立一組觸 發器和存儲過程來支援這種能力。為了運作aspnet_regsql工具,隻需打開Visual Studio指令提示行,然後輸入類似下列的指令:

D:\>aspnet_regsql –S localhost –U sa –P thiru –d Pubs –ed

運作正常的話,應該顯示如下:

Enabling the database for SQL cache dependency.

Finished.

D:\>

該指令使Pubs資料庫支援SQL緩存無效功能。其中: ◆S—伺服器名

◆U—用于連接配接到SQL Server的使用者ID

◆P—用于連接配接到SQL Server的使用者密碼

◆d—資料庫名

◆ed—支援該資料庫具有SQL Server觸發的緩存無效功能 一旦在資料庫級上完成上面的操作,接下來,你需要在單個表級上啟動緩存無效功能。具體實作請參考下列指令:

D:\>aspnet_regsql –S localhost –U sa –P thiru –t authors –d Pubs –et

運作正常的話,應該顯示如下:

Enabling the table for SQL cache dependency.

Finished.

D:\>

在上面的指令中: ◆t—指定表的名稱

◆et—支援該表具有SQL Server觸發的緩存無效功能 上面的指令展示了如何為Pubs資料庫中的authors表啟動SQL緩存無效支援。一旦你配置好authors表可以發送通知,那麼,任何時候該表中的資料變化時,它都會通知ASP.NET使位于緩存中的相應的項無效。 五、為SQL緩存無效實作Web配置 下一步,在你能夠把SQL緩存無效功能使用于你的ASP.NET應用程式之前,還要更新Web配置檔案—你需要訓示ASP.NET架構查詢你已經啟動SQL緩存無效功能的資料庫。下列Web配置檔案包含以特定的時間間隔周期性地查詢Pubs資料庫中必要的配置資訊:

<configuration>

<connectionStrings>

<add name="Pubs"

connectionString="Server=localhost;Database=Pubs;uid=sa;

pwd=thiru" />

</connectionStrings>

<system.web>

<caching>

<sqlCacheDependency enabled="true">

<databases>

<add name="Pubs"

connectionStringName="Pubs"

pollTime="60000" />

</databases>

</sqlCacheDependency>

</caching>

</system.web>

</configuration>

前面的Web配置檔案包含兩個節—<connectionStrings>和<caching>。其中, connectionStrings節建立一個名為“Pubs”的到Pubs資料庫的資料庫連接配接字元串;caching節實作配置SQL緩存無效查詢;在 databases子節下,列出一個或多個你想查詢其變化情況的資料庫;位于databases節中的add節顯示Pubs資料庫每一分鐘(每60, 000毫秒)被查詢一次。你可以為不同的資料庫指定不同的查詢間隔時間。記住,每當查詢資料庫變化情況時,伺服器都必須多做一些工作。如果你不希望資料庫 中的資料經常改變的話,那麼,你應該增加查詢間隔時間。 六、在SqlDataSource控件中實作SQL緩存無效 現在,既然你已經實作了要求的配置,那麼接下來,你就可以在你的ASP.NET Web頁面中利用SQL緩存無效特征。為此,隻需要把一個新的Web表單SqlCacheInvalidation.aspx添加到你的網站上。下列是相 應于SqlCacheInvalidation.aspx的代碼:

<%@ Page Language="C#" %>

<html>

<head>

<title>SQL緩存無效示例</title>

</head>

<body>

<form id="form1" runat="server">

<asp:GridView ID="GridView1" Runat="server"

DataSourceID="SqlDataSource1" DataKeyNames="au_id"

AutoGenerateColumns="False">

<Columns>

<asp:CommandField ShowEditButton="True" />

<asp:BoundField ReadOnly="True" HeaderText="timestamp"

DataField="timestamp" SortExpression="timestamp" />

<asp:BoundField ReadOnly="True" HeaderText="au_id"

DataField="au_id" SortExpression="au_id" />

<asp:BoundField HeaderText="au_lname" DataField="au_lname"

SortExpression="au_lname" />

<asp:BoundField HeaderText="au_fname" DataField="au_fname"

SortExpression="au_fname" />

<asp:BoundField HeaderText="phone" DataField="phone"

SortExpression="phone" />

<asp:BoundField HeaderText="address" DataField="address"

SortExpression="address" />

<asp:BoundField HeaderText="city" DataField="city"

SortExpression="city" />

<asp:BoundField HeaderText="state" DataField="state"

SortExpression="state" />

<asp:BoundField HeaderText="zip" DataField="zip"

SortExpression="zip" />

<asp:CheckBoxField HeaderText="contract"

SortExpression="contract"

DataField="contract" />

</Columns>

</asp:GridView>

<asp:SqlDataSource ID="SqlDataSource1" Runat="server"

SelectCommand="SELECT DatePart(second, GetDate())

As timestamp, * FROM [authors]" ConnectionString="<%$

ConnectionStrings:Pubs %>" EnableCaching="True"

DeleteCommand="DELETE FROM [authors]

WHERE [au_id] = @original_au_id"

InsertCommand="INSERT INTO [authors] ([au_id], [au_lname],

[au_fname], [phone], [address], [city], [state], [zip],

[contract]) VALUES (@au_id, @au_lname, @au_fname, @phone,

@address, @city, @state, @zip, @contract)"

UpdateCommand="UPDATE [authors] SET [au_lname] = @au_lname,

[au_fname] = @au_fname, [phone] = @phone,

[address] = @address, [city] = @city, [state] = @state,

[zip] = @zip, [contract] = @contract

WHERE [au_id] = @original_au_id"

SqlCacheDependency="Pubs:authors">

<DeleteParameters>

<asp:Parameter Name="original_au_id" />

</DeleteParameters>

<UpdateParameters>

<asp:Parameter Type="String" Name="au_lname" />

<asp:Parameter Type="String" Name="au_fname" />

<asp:Parameter Type="String" Name="phone" />

<asp:Parameter Type="String" Name="address" />

<asp:Parameter Type="String" Name="city" />

<asp:Parameter Type="String" Name="state" />

<asp:Parameter Type="String" Name="zip" />

<asp:Parameter Type="Boolean" Name="contract" />

<asp:Parameter Name="original_au_id" />

</UpdateParameters>

<InsertParameters>

<asp:Parameter Type="String" Name="au_id" />

<asp:Parameter Type="String" Name="au_lname" />

<asp:Parameter Type="String" Name="au_fname" />

<asp:Parameter Type="String" Name="phone" />

<asp:Parameter Type="String" Name="address" />

<asp:Parameter Type="String" Name="city" />

<asp:Parameter Type="String" Name="state" />

<asp:Parameter Type="String" Name="zip" />

<asp:Parameter Type="Boolean" Name="contract" />

</InsertParameters>

</asp:SqlDataSource>

</form>

</body>

</html>

上面的代碼在SqlDataSource控件上施加了SQL緩存無效功能。你已經看到,這個SqlDataSource控件包含EnableCaching和SqlCacheDependency兩個屬性。其中,SqlCacheDependency屬性使用下列文法:

SqlCacheDependency="Pubs:authors"

在這個屬性聲明中,你首先列舉出資料庫的名稱,後面跟着的是資料庫表名。由于這一屬性,無論何時Pubs資料庫中的authors表中的資料改變, 緩沖資料都會自動地無效。注意,你在此指定的資料庫名應該已經定義在web.config檔案的connectionStrings節中。 作為SqlDataSource控件聲明的一部分,你還要指定SelectCommand,InsertCommand, DeleteCommand,UpdateCommand和ConnectionString屬性。這些xxxCommand屬性允許你指定要被執行的 SQL指令,而ConnectionString屬性允許你指定用于連接配接到Pubs資料庫的連接配接字元串;另外,這個值還可以從web.config檔案中 進行檢索。因為該xxxCommand屬性也包含SQL查詢參數,是以,它們可以在DeleteParameters,UpdateParameters 和InsertParameters模闆中指定。 最後,聲明一個GridView控件并且使用DataSourceID屬性把GridView控件綁定到SqlDataSource控件上。通過把 GridView的DataSourceID屬性設定為SqlDataSource控件的ID,從SqlDataSource傳回的資料就可以被自動地顯 示。 現在,代碼分析完畢;那麼,接下來,你可以啟動浏覽器導航到該頁面來測試這種緩沖功能。你将看到類似下圖1的輸出結果:

深入剖析ASP.NET 2.0緩沖機制

  圖1:在SqlDataSource控件中實作SQL緩存無效運作結果 其中,這裡的timestamp欄顯示資料庫日期的“秒”部分。如果你再次重新整理該頁面,你将在随後顯示的輸出中看到相同的時間,因為你已啟動了該SqlDataSource控件中的緩沖功能。 現在,為了測試這種基于SQL Server的觸發器無效效果,你可以點選一個Edit超級連結并更新相應的作者資訊。之後,你應該在顯示于上面的頁面中的timestamp中看到發生 了改變。這個例子非常清晰地展示了,一旦authors表中的資料發生變化,你的基于SQL Server的觸發器無效機制将自動地使SqlDataSource控件中的緩沖内容無效。 七、部分頁面緩沖技術 到目前為止,你已經看到了如何在SqlDataSource控件中使用緩沖技術。本節将向你展示如何使用回寄方式緩存Substitution以便 用重新整理的内容替換被緩沖的内容部分。這稱作“部分頁面緩沖”或“頁面片斷緩沖”。這是一種強有力的特征,它允許應用程式使用頁面級緩沖—即使該頁面的部分 是動态生成的。 頁面片斷緩沖提供對頁面中一個片斷進行緩沖的功能,這與緩沖整個頁面形成對照。有時,整頁面輸出緩沖是不可行的—例如,當該頁面的部分需要針對每一 個使用者請求動态地建立時。在這些情況下,辨別出該頁面或控件中不經常改變的部分以及那些需要耗費相應的伺服器時間資源才能建立的部分可能是非常有價值的。 在你辨別這些部分後,你就能夠把它們包裝到一個Web表單使用者控件中并且緩存該控件;這樣以來,頁面中的這些部分就不需要每次被重建。這是在 ASP.NET 2.0以前實作頁面片斷緩沖的唯一方法。借助于ASP.NET 2.0中的新的回寄緩存Substitution特征,在頁面呈現給使用者之前,你可以通知ASP.NET運作時刻重新計算一個特定元素(正當它被生成到一 個緩沖頁面上時)。為此,你可以使用兩種方案來實作這一目的: ◆調用新的Response.WriteSubstitution方法,同時傳遞一個到Substitution回調函數的引用。 ◆把一個<asp:substitution>控件添加到Web頁面并且把MethodName屬性設定為該回調函數名。 無論使用哪種方案,你都需要在該頁面的頂部放一條@Output緩存指令以指定相應的持續時間,而且該依賴性的其它的參數也應該被添加到該頁面上。在這個示例中,我們選用Substitution控件。 該Substitution控件具有一個重要的屬性—MethodName,它用于指定被調用以提供動态内容的方法。注意,Substitution控件調用的方法必須是一個靜态方法。 而且,該方法必須有一個代表目前HttpContext的參數。為了說明這個問題,我們建立一個新的Web表單PartialPageCaching.aspx并把它添加到網站上。下面是示例PartialPageCaching.aspx相應的實作代碼:

<%@ Page Language="C#" %>

<%@ OutputCache Duration="6000" VaryByParam="none" %>

<script runat="server">

public static string GetRandomNumber(HttpContext context)

{

int randomNumber;

randomNumber = new System.Random().Next(1, 10000);

return randomNumber.ToString();

}

</script>

<html>

<head>

<title>Partial Page Caching using Substitution control</title>

</head>

<body>

<form id="form1" runat="server">

The random number generated is:

<asp:Substitution ID="Substitution1" MethodName="GetRandomNumber"

Runat="Server"/>

<p>

The current time is

<%= DateTime.Now.ToString("t") %>. It never changes since the

page is cached.

</p>

</form>

</body>

</html>

在該頁面的頂部,Output緩存指令在記憶體中緩沖該頁面的内容。在Output緩存指令中,你把Duration屬性設定為6,000毫秒。 VaryByParam屬性顯示,在進行緩沖時ASP.NET是否應該考慮這些頁面參數。當把VaryByParam設定為none時,卻意味着不考慮任 何參數;所有的使用者将接收相同的頁面而不考慮提供的其它參數(參考下圖2的輸出結果)。

深入剖析ASP.NET 2.0緩沖機制

  圖2:部分頁面緩沖運作效果圖 Substitution控件的MethodName屬性被設定為一個方法GetRandomNumber,它簡單地傳回一個在1~10,000之 間的随機數字。當你向該頁面發出一個請求時,被顯示的目前時間總是保持不變,而被該Substitution控件生成的頁面部分卻每次都保持改變。在這種 情況下,在每當有人請求該頁面時,它顯示一個在1~10,000之間的随機數字。 八、結論 ASP.NET 2.0提供的緩存API基于ASP.NET 1.0基礎之上,并且有助于我們實作建構高性能的ASP.NET應用程式的理想。ASP.NET 2.0提供的新的資料源控件中提供了使在記憶體中緩沖資料庫資料非常容易的屬性。借助于這些控件,你可以檢索并緩沖資料庫資料而不需編寫一行代碼。當資料庫 中的資料改變時能夠無效一個緩沖項,這是微軟開發人員經過長期探索才找到的一種革命化ASP.NET應用程式建構和釋出的方式。最後,新的 Substitution控件有助于使你很容易地把動态的内容注入到一個緩沖頁面中。

本文轉自朱先忠老師51CTO部落格,原文連結: http://blog.51cto.com/zhuxianzhong/59819 ,如需轉載請自行聯系原作者