程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> ASP.NET >> ASP.NET基礎 >> 在ASP.NET 2.0中操作數據之五十九:使用SQL緩存依賴項SqlCacheDependency

在ASP.NET 2.0中操作數據之五十九:使用SQL緩存依賴項SqlCacheDependency

編輯:ASP.NET基礎

導言:

  在56和57章探討的緩存技術使用的是基於時間的緩存周期,當過了某段時間後便將緩存數據從內存清除。當設置緩存時間為x秒時,數據在x秒內都是“新”的。當然,就像在60章談到的那樣,對靜態數據來說,x可延伸到web應用程序的整個生命周期(lifetime)。

  當緩存數據時,基於時間周期的技術因為其易用性而常常被采用,不過又常常不那麼完美。理想的狀態是這樣的:數據庫數據還是應緩存在內存,直到源數據(underlying data)發生改變時才從內存清除。這樣的話可以最大化的獲取緩存帶來的性能上的好處,同時使“過時數據”(stale data)持續的時間最短。然而,為此,我們需要建立一種機制來探測數據庫數據什麼時候發生了改變,並將對應的緩存條目清除掉。

  ASP.NET 2.0提供的SqlCacheDependency class類和必要的下部基礎構造(infrastructure)可以判斷數據庫什麼時候發生了更改,以便將對應的緩存條目從內存清除掉。有2種技術可以判斷源數據在什麼時候發生了改變:polling 和notification。討論完這2者之間的差別後,我們將創建必要的下部基礎構造來支持polling,然後探討如何使用SqlCacheDependency class類

理解Notification and Polling

  正如前面所述,有2種方法來判斷一個數據庫裡的數據在什麼時候修改過:notification 和 polling.當使用notification的時候,數據庫提示(alerts)ASP.NET對應某個具體查詢的數據已經發生了改變;於是對應的緩存條目將被清除。使用polling的時候,數據庫服務器將包含某個表(tables)最近發生更改時的相關信息。ASP.NET周期性的對數據庫進行檢查,看哪些表在數據被緩存以後發生過改動,若改動過,對應的緩存條目將被清除。

  notification是對查詢(query)而不是表(table)進行跟蹤檢查,相對polling而言,需要采取的步驟要少些。不過遺憾的是,只有在Microsoft SQL Server 2005的完整版(也就是non-Express版本)才能使用該功能。而Microsoft SQL Server的所有版本,從7.0 到2005都可以使用polling功能,因為本系列教程使用的是SQL Server 2005的Express版本,在此我們將集中探討建立和使用polling。關於SQL Server 2005的notification功能,你可以參閱本文結束部分的Further Reading。

  要使用polling,我們將設置數據庫包含一個名為AspNet_SqlCacheTablesForChangeNotification的表。該表有3列:tableName, notificationCreated, 和changeId.對於哪些在web應用程序的SQL cache dependency裡要用到的表,該表都有一條記錄與之對應。tableName就是具體某個表的名稱;notificationCreated指明了添加記錄時的date 和 time;而列changeId的類型是int,初始值是0,每當對應的表發生一次改動,其值就自動增加一次。

  除了表AspNet_SqlCacheTablesForChangeNotification外,數據庫還需要為出現在SQL cache dependency裡的每個表包含一個觸發器(triggers),任何時候,只要表插入、更新、刪除一條記錄或在表AspNet_SqlCacheTablesForChangeNotification裡的對應的changeId值增大的情況下就會執行觸發器。

  當使用SqlCacheDependency對象(object)來緩存數據時,ASP.NET將關注某個表的當前(current)的changeId值,一旦發現當前其值與數據庫裡面的changeId值不同時,就將該SqlCacheDependency對象清除。因為,changeId不吻合就意味著在完成數據緩存後,表又發生過改動。

第一步:考察命令行程序aspnet_regsql.exe

  如上所述,使用polling方法時,必須對數據庫進行設置以包含這些基礎構造:一個預先定義的表(AspNet_SqlCacheTablesForChangeNotification),一些存儲過程,以及基於在SQL cache dependencies裡要用到的表的觸發器。諸如這些表、存儲過程、觸發器等都可以通過命令行程序aspnet_regsql.exe來創建,該命令位於$WINDOWS$/Microsoft.NET/Framework/version文件夾。要創建表AspNet_SqlCacheTablesForChangeNotification以及相關的存儲過程,可以在命令行這樣運行:

復制代碼 代碼如下:/* For SQL Server authentication... */
aspnet_regsql.exe -S server -U user -P password -d database -ed

復制代碼 代碼如下:/* For Windows Authentication... */
aspnet_regsql.exe -S server -E -d database -ed

  注意:要運行這些命令,必須以db_securityadmin 和 db_ddladmin的身份登錄數據庫,更多詳情請參閱作者博客:http://scottonwriting.net/sowblog/posts/10709.aspx

比如:在Windows身份認證模式下,對某個數據庫服務器ScottsServer裡的數據庫pubs添加基礎構造時,在命令行鍵入:

復制代碼 代碼如下:aspnet_regsql.exe -S ScottsServer -E -d pubs -ed

完成了數據庫級(database-level)基礎構造的添加後,我們需要添加觸發器,再次使用aspnet_regsql.exe命令,不過用-t來指定"表名"(table name),且將-ed替換為-et,如下:

復制代碼 代碼如下:/* For SQL Server authentication... */
aspnet_regsql.exe -S <i>server</i>
-U <i>user</i> -P <i>password</i> -d <i>database</i> -t <i>tableName</i> -et

復制代碼 代碼如下:/* For Windows Authentication... */
aspnet_regsql.exe -S <i>server</i>
-E -d <i>database</i> -t <i>tableName</i> -et

要對ScottsServer裡的表authors和titles添加觸發器,這樣做:

復制代碼 代碼如下:aspnet_regsql.exe -S ScottsServer -E -d pubs -t authors -et
aspnet_regsql.exe -S ScottsServer -E -d pubs -t titles -et

就本文而言,我們要對表Products, Categories,和Suppliers添加觸發器,具體的命令在第三步探討。

第二步:在文件夾App_Data裡引用一個Microsoft SQL Server 2005 Express版的數據庫

  我們剛剛說過,為了添加必要的基礎構造,aspnet_regsql.exe命令需要用到數據庫和服務器的名稱。但是對於放在文件夾App_Data裡的一個Microsoft SQL Server 2005 Express的數據庫而言,它的數據庫名和服務器名又是什麼呢?犯不著探究其數據庫名和服務器名到底是什麼,我發現最簡單的方法是用SQL服務管理器(SQL Server Management Studio)來將該數據庫認作localhost/SQLExpress database數據庫,並重新命名。如果你的機器裡已經安裝了SQL Server 2005完整版,自然也就安裝了SQL服務管理器。如果你安裝的是Express版本的話,你可以免費下載Microsoft SQL Server Management Studio Express Edition.
(http://www.microsoft.com/downloads/details.aspx?displaylang=en&FamilyID=C243A5AE-4BD1-4E3D-94B8-5A0F62BF7796)

首先,關閉Visual Studio,然後打開SQL Server Management Studio,在Windows Authentication模式裡選擇連接到localhost/SQLExpress.

https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017010916404256.gif
圖1:連接到localhost/SQLExpress Server

  連接到服務器後,管理器將顯示服務器,並將數據庫、安全等以折疊的形式顯示出來。在數據庫文件夾上右擊,選添加(Attach)項,這樣將彈出Attach Databases對話框(見圖2),點Add按鈕,選擇我們的web應用程序的App_Data文件夾裡的NORTHWND.MDF數據庫。

https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017010916404338.gif
圖2:選App_Data文件夾裡的NORTHWND.MDF數據庫

  這樣將會把數據庫添加到Databases文件夾,且數據庫的名稱可能是該數據庫文件的絕對路徑(full path).出於簡便的原則,我們將其重命名為一個更友好(human-friendly)的名字,我將其命名為“DataTutorials”.

https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017010916404952.gif
圖3:將新添加的數據庫重命名

第三步:對Northwind數據庫添加Polling基礎構造

現在我們添加了App_Data文件夾裡的NORTHWND.MDF數據庫,讓我們添加polling 基礎構造吧,假定你已經將數據庫重命名為“DataTutorials”, 運行如下的命令:

復制代碼 代碼如下:aspnet_regsql.exe -S localhost/SQLExpress -E -d DataTutorials -ed
aspnet_regsql.exe -S localhost/SQLExpress -E -d DataTutorials -t Products -et
aspnet_regsql.exe -S localhost/SQLExpress -E -d DataTutorials -t Categories -et
aspnet_regsql.exe -S localhost/SQLExpress -E -d DataTutorials -t Suppliers -et

完成上述4個命令後,在Management Studio裡右擊數據庫,進入任務子菜單(Tasks submenu),選分派(Detach)。然後關閉Management Studio並重新打開Visual Studio.

打開Visual Studio後,在服務器資源管理器裡展開數據庫,你可以看到有新增的表(AspNet_SqlCacheTablesForChangeNotification),新的存儲過程,以及對應於表Products, Categories, 和Suppliers的觸發器.

https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017010916405018.gif
圖4:數據庫包含了必需的Polling基礎構造

第四步:設置Polling服務

完成上述步驟後,最後我們需要設置polling服務。這要用到Web.config文件,在裡面指定要用到的數據庫,以及檢測頻率(polling frequency),單位為毫秒。下面的代碼是每隔1秒對Northwind數據庫檢測一次。

<?xml version="1.0"?>
<configuration>
 <connectionStrings>
 <add name="NORTHWNDConnectionString" connectionString=
  "Data Source=./SQLEXPRESS;AttachDbFilename=|DataDirectory|/NORTHWND.MDF;
  Integrated Security=True;User Instance=True"
  providerName="System.Data.SqlClient"/>
 </connectionStrings>
 <system.web>
 ...

 <!-- Configure the polling service used for SQL cache dependencies -->
 <caching>
  <sqlCacheDependency enabled="true" pollTime="1000" >
  <databases>
  <add name="NorthwindDB"
   connectionStringName="NORTHWNDConnectionString" />
  </databases>
  </sqlCacheDependency>
 </caching>
 </system.web>
</configuration>

  在<add>元素裡的name值(“NorthwindDB”)是一個易讀(human-readable)的名稱,它與某個具體的數據庫對應。當使用SQL cache dependencies的時候,我們需要引用在這裡定義的數據庫名。我們將在第六步考察怎樣使用SqlCacheDependency class類來緩存數據。

  一旦確定了一個SQL cache dependency後,檢測系統(polling system)每隔定義的pollTime那麼多毫秒對<databases>元素裡的數據庫進行連接,並執行名為AspNet_SqlCachePollingStoredProcedure的存儲過——該存儲過程是我們在第三步使用aspnet_regsql.exe命令行工具添加的,它返回的是表AspNet_SqlCacheTablesForChangeNotification裡的每條記錄的tableName 和 changeId值。那些“過時”的SQL cache dependencies將會從內存清除掉。

  應在權衡性能和數據刷新(data staleness)的基礎上設置pollTime.較小的pollTime值雖然導致請求數據庫的次數增加,但能更快的將“過時"的數據清除掉;較大的pollTime值雖然減少了對數據庫的請求次數,但增加了“過時”的緩存條目的呆滯時間。還好,對數據庫的請求只是執行一個簡單的存儲過程而已,該存儲過程僅僅從一個簡單的表返回很少的幾行。你最好多測試幾個不同的pollTime值,在平衡數據庫訪問和數據刷新2方面的情況下找出一個理想的值。pollTime值最小允許為500.

  注意:在上面的代碼裡,我們在<sqlCacheDependency>元素裡指定了一個單一的pollTime值。其實你也可以在<add>元素裡隨意的指定一個pollTime值。當你指定了很多個數據庫,且你想為每個數據庫都指定一個檢測頻率(polling frequency)時,這樣做很有用。

第五:聲明SQL Cache Dependencies

  在第一到第四步驟,我們探討了如何建立必需的數據庫基礎構造,以及設置檢測系統(polling system).完成上述步驟後,現在我們可以通過編程或聲明的方式,在添加緩存條目時使用SQL cache dependency.在本節,我們探討如何使用聲明的方式使用SQL cache dependencies,在第六步再探討通過編程的方式。

  在《使用ObjectDataSource緩存數據》教程裡,我們考察了聲明ObjectDataSource控件的緩存功能。僅僅將EnableCaching屬性設置為true,並將acheDuration屬性設置為某個時間間(timeinterval),ObjectDataSource控件就會自動地將從“源對象”(underlying object)返回的數據進行緩存。ObjectDataSource控件可以使用單個或多個SQL cache dependencies.

  為此,打開文件夾Caching裡的SqlCacheDependencies.aspx頁面,在設計模式裡,從工具箱拖一個GridView控件到頁面上,設置其ID為ProductsDeclarative ,從其智能標簽裡將其綁定到一個名為ProductsDataSourceDeclarative的ObjectDataSource.

https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017010916405080.gif
圖5:創建一個名為ProductsDataSourceDeclarative的ObjectDataSource

  設置該ObjectDataSource使用ProductsBLL類。在SELECT標簽裡選GetProducts()方法;在UPDATE標簽裡,選擇包含3個輸入參數——productName,unitPrice,和productID的UpdateProduct重載方法;在INSERT 和 DELETE標簽裡選“(None)”.

https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017010916405180.gif
圖6:使用包含3個輸入參數的UpdateProduct重載方法

https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017010916405123.gif
圖7:在INSERT和DELETE標簽的下拉列表裡選“(None)”

  完成設置後,Visual Studio會為GridView裡的每一列創建綁定列(BoundFields) 和CheckBoxFieldsL列。將ProductName, CategoryName, 和UnitPrice以外的列都刪除,對其應用什麼格式化都可以。在GridView的智能標簽裡啟用分頁、排序、編輯功能。Visual Studio會將ObjectDataSource控件的OldValuesParameterFormatString屬性設置為original_{0},為使GridView的編輯功能運行正常,要麼刪除該屬性,要麼將其設置為默認值:{0}.

  最後,在GridView上面添加一個Label Web控件,設置其ID為ODSEvents,再將其EnableViewState屬性設置為false.做完上述修改後,頁面的聲明代碼看起來應該和下面的差不多。注意,我已經對GridView列的外觀做了些定制,雖然這對SQL cache dependency功能來說並不是必要的。

<asp:Label ID="ODSEvents" runat="server" EnableViewState="False" />

<asp:GridView ID="ProductsDeclarative" runat="server"
 AutoGenerateColumns="False" DataKeyNames="ProductID"
 DataSourceID="ProductsDataSourceDeclarative"
 AllowPaging="True" AllowSorting="True">
 <Columns>
 <asp:CommandField ShowEditButton="True" />
 <asp:TemplateField HeaderText="Product" SortExpression="ProductName">
  <EditItemTemplate>
  <asp:TextBox ID="ProductName" runat="server"
   Text='<%# Bind("ProductName") %>' />
  <asp:RequiredFieldValidator ID="RequiredFieldValidator1"
   ControlToValidate="ProductName" Display="Dynamic"
   ErrorMessage="You must provide a name for the product."
   SetFocusOnError="True"
   runat="server">*</asp:RequiredFieldValidator>
  </EditItemTemplate>
  <ItemTemplate>
  <asp:Label ID="Label2" runat="server"
   Text='<%# Bind("ProductName") %>' />
  </ItemTemplate>
 </asp:TemplateField>
 <asp:BoundField DataField="CategoryName" HeaderText="Category"
  ReadOnly="True" SortExpression="CategoryName" />
 <asp:TemplateField HeaderText="Price" SortExpression="UnitPrice">
  <EditItemTemplate>
  $<asp:TextBox ID="UnitPrice" runat="server" Columns="8"
   Text='<%# Bind("UnitPrice", "{0:N2}") %>'></asp:TextBox>
  <asp:CompareValidator ID="CompareValidator1" runat="server"
   ControlToValidate="UnitPrice"
   ErrorMessage="You must enter a valid currency value with
   no currency symbols. Also, the value must be greater than
   or equal to zero."
   Operator="GreaterThanEqual" SetFocusOnError="True"
   Type="Currency" Display="Dynamic"
   ValueToCompare="0">*</asp:CompareValidator>
  </EditItemTemplate>
  <ItemStyle HorizontalAlign="Right" />
  <ItemTemplate>
  <asp:Label ID="Label1" runat="server"
   Text='<%# Bind("UnitPrice", "{0:c}") %>' />
  </ItemTemplate>
 </asp:TemplateField>
 </Columns>
</asp:GridView>

<asp:ObjectDataSource ID="ProductsDataSourceDeclarative" runat="server"
 SelectMethod="GetProducts" TypeName="ProductsBLL"
 UpdateMethod="UpdateProduct">
 <UpdateParameters>
 <asp:Parameter Name="productName" Type="String" />
 <asp:Parameter Name="unitPrice" Type="Decimal" />
 <asp:Parameter Name="productID" Type="Int32" />
 </UpdateParameters>
</asp:ObjectDataSource>

下一步,為ObjectDataSource控件的Selecting事件創建一個事件處理器:

protected void ProductsDataSourceDeclarative_Selecting
 (object sender, ObjectDataSourceSelectingEventArgs e)
{
 ODSEvents.Text = "-- Selecting event fired";
}

  我們知道,只有當ObjectDataSource控件從它的相關“源對象”(underlying object)獲取數據時才會觸發它的Selecting事件。如果ObjectDataSource是從內存檢索數據的話,將不會觸發Selecting事件.

  現在,在浏覽器裡登錄該頁面,因為我們還沒有進行緩存,所以每當你分頁、排序、編輯時,頁面都會顯示文本——“—Selecting event fired”, 如圖8所示:

https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017010916405141.gif
圖8:當分頁、排序、編輯時都會觸發ObjectDataSource的Selecting事件。

  就像我們在教程《使用ObjectDataSource緩存數據》裡探討的一樣,除了EnableCaching屬性以外,ObjectDataSource控件還有SqlCacheDependency property屬性,它可以為緩存數據添加一個或更多的SQL cache dependencies.像下面這樣:

databaseName1:tableName1;databaseName2:tableName2;...

  其中,databaseName是Web.config文件裡<add>元素的name屬性指定的數據庫名,而tableName就是數據庫裡的一個表。舉個例,要創建一個ObjectDataSource,它用SQL cache dependency來緩存數據,當我們指定要用Northwind數據庫裡的 Products表時,我們將ObjectDataSource的EnableCaching屬性設置為true,且SqlCacheDependency屬性為“NorthwindDB:Products”.
注意:你可以通過設置EnableCaching屬性為true來使用一個 SQL cache dependency和基於時間的緩存期(time-based expiry)。CacheDuration對應時間間隔;SqlCacheDependency對應數據庫名和表名。不管是緩存到期還是檢查系統(polling system)發現“源數據”發生改變,只要其中一個發生, ObjectDataSource 都會將清除其數據。

  在SqlCacheDependencies.aspx頁面裡的GridView 控件從2個表獲取數據——Products 和 Categories (產品的CategoryName列是通過語法 JOIN on Categories來獲取的). 因此,我們想指定2個SQL cache dependencies:
“NorthwindDB:Products;NorthwindDB:Categories”.

https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017010916405273.gif
圖9:設置ObjectDataSource支持緩存,且使用基於表Products 和 Categories的SQL Cache Dependencies

  設置SQL Cache Dependencies支持緩存後,再次來浏覽器裡登錄頁面。最開始,文本“—Selecting event fired”依然會出現在頁面裡,但當進行分頁、排序或點擊編輯和取消按鈕時,文本就消失了。這是因為對數據進行緩存後,其緩存狀態一直持續,直到Products 或 Categories表發生了改變,或我們通過GridView對數據進行了更新。

  做個實驗,在第一個浏覽器窗口進行分頁操作,請注意文本“—Selecting event fired”並沒有顯現出來。再打開第2個浏覽器窗口,導航到頁面Basics.aspx頁面(~/EditInsertDelete/Basics.aspx). 對某個產品的name 或 price進行更新。 再次返回到第一個浏覽器窗口,查看下一個頁面或進行排序操作或點擊某行的編輯按鈕,這一次,文本“—Selecting event fired又出現了,這是因此“源數據”發生了更改(見圖10)。如果文本沒有出現,請稍等一下再試一回。我們知道,polling服務每隔設定的pollTime那麼多毫秒對Products表進行檢查,開是否改動過。因此在源數據的更新和“過時”數據的清除之間有個延遲期。

https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017010916405276.gif
圖10:改動Products表將導致清除“過時”的Product緩存數據

第六步:通過編程的方式處理SqlCacheDependency類

  在教程《在分層架構中緩存數據》我們看到了使用單獨的緩存層 Caching Layer的好處。在那篇教程,我們創建了一個ProductsCL 類來處理data cache.要在緩存層Caching Layer利用SQL cache dependencies的話,要用到SqlCacheDependency 類。

  在檢測系統(polling system)裡,一個SqlCacheDependency對象必須與某個具體的數據庫和表掛鉤。下面的代碼,創建了一個SqlCacheDependency對象,它基於Northwind數據庫的Products表:

Caching.SqlCacheDependency productsTableDependency =
 new Caching.SqlCacheDependency("NorthwindDB", "Products");

  上面的2個參數分別對應數據庫名和表名。與ObjectDataSource控件的屬性SqlCacheDependency類似,數據庫名是使用的Web.config.文件裡<add> 元素的name屬性指定的值,而表名是實際的數據庫表名.。

  要將一個SqlCacheDependency與添加到內存的條目聯系起來,可以使用一個重載的接受dependency的Insert方法。下面代碼裡的SqlCacheDependency基於表Products,且緩存時間未定。換句話說,數據會一直保存在內存,除非內存不足或表Products發生了改變才被清除掉。

Caching.SqlCacheDependency productsTableDependency =
 new Caching.SqlCacheDependency("NorthwindDB", "Products");
Cache.Insert(key,
  value,
  productsTableDependency,
  System.Web.Caching.Cache.NoAbsoluteExpiration,
  System.Web.Caching.Cache.NoSlidingExpiration);

  目前,緩存層Caching Layer的ProductsCL類從表Products獲取數據,緩存時間為60秒。 讓我們對其進行更新,使其使用SQL cache dependencies. 類ProductsCL的AddCacheItem方法是用來向內存添加數據的,其當前代碼如下:

private void AddCacheItem(string rawKey, object value)
{
 System.Web.Caching.Cache DataCache = HttpRuntime.Cache;

 // Make sure MasterCacheKeyArray[0] is in the cache
 DataCache[MasterCacheKeyArray[0]] = DateTime.Now;

 // Add a CacheDependency
 Caching.CacheDependency dependency =
 new Caching.CacheDependency(null, MasterCacheKeyArray);
 DataCache.Insert(GetCacheKey(rawKey), value, dependency,
 DateTime.Now.AddSeconds(CacheDuration),
 System.Web.Caching.Cache.NoSlidingExpiration);
}

  讓我們對其進行更新,用一個SqlCacheDependency對象來替換掉MasterCacheKeyArray cache dependency:

  我們來進行測試。在名為ProductsDeclarative的GridView控件下再添加一個GridView,設置其ID為ProductsProgrammatic,在其智能標裡將其綁定到一個名為ProductsDataSourceProgrammatic的新的ObjectDataSource,設置該ObjectDataSource使用ProductsCL類,分別在SELECT 和 UPDATE標簽裡選GetProducts 和 UpdateProduct方法。

https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017010916405379.gif
圖11:設置新ObjectDataSource使用ProductsCL類

https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017010916405443.gif
圖12:在SELECT標簽裡選GetProducts方法

https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017010916405490.gif
圖13:在UPDATE標簽選UpdateProduct方法

  完成設置後,Visual Studio會自動地為GridView控件添加BoundFields和 CheckBoxFields。就像上面那個GridView控件一樣,將ProductName, CategoryName, 和 UnitPrice以外的列都刪除掉。在其智能標簽裡,啟用分頁、排序、編輯功能。同時,為使GridView控件的編輯功能正常工作,將OldValuesParameterFormatString屬性改成默認值{0}. 或干脆在代碼聲明裡將該屬性刪除。

完成上述修改後,最終的GridView 和 ObjectDataSource的聲明代碼看起來應該和下面的差不多:

<asp:GridView ID="ProductsProgrammatic" runat="server"
 AutoGenerateColumns="False" DataKeyNames="ProductID"
 DataSourceID="ProductsDataSourceProgrammatic" AllowPaging="True"
 AllowSorting="True">
 <Columns>
 <asp:CommandField ShowEditButton="True" />
 <asp:TemplateField HeaderText="Product" SortExpression="ProductName">
  <EditItemTemplate>
  <asp:TextBox ID="ProductName" runat="server"
   Text='<%# Bind("ProductName") %>' />
  <asp:RequiredFieldValidator ID="RequiredFieldValidator1" 
   ControlToValidate="ProductName" Display="Dynamic"
   ErrorMessage="You must provide a name for the product."
   SetFocusOnError="True"
   runat="server">*</asp:RequiredFieldValidator>
  </EditItemTemplate>
  <ItemTemplate>
  <asp:Label ID="Label2" runat="server"
   Text='<%# Bind("ProductName") %>' />
  </ItemTemplate>
 </asp:TemplateField>
 <asp:BoundField DataField="CategoryName" HeaderText="Category"
  ReadOnly="True" SortExpression="CategoryName" />
 <asp:TemplateField HeaderText="Price" SortExpression="UnitPrice">
  <EditItemTemplate>
  $<asp:TextBox ID="UnitPrice" runat="server" Columns="8"
   Text='<%# Bind("UnitPrice", "{0:N2}") %>'></asp:TextBox>
  <asp:CompareValidator ID="CompareValidator1" runat="server"
   ControlToValidate="UnitPrice" Display="Dynamic"
   ErrorMessage="You must enter a valid currency value with
   no currency symbols. Also, the value must be greater than
   or equal to zero."
   Operator="GreaterThanEqual" SetFocusOnError="True"
   Type="Currency" ValueToCompare="0">*</asp:CompareValidator>
  </EditItemTemplate>
  <ItemStyle HorizontalAlign="Right" />
  <ItemTemplate>
  <asp:Label ID="Label1" runat="server"
   Text='<%# Bind("UnitPrice", "{0:c}") %>' />
  </ItemTemplate>
 </asp:TemplateField>
 </Columns>
</asp:GridView>

<asp:ObjectDataSource ID="ProductsDataSourceProgrammatic" runat="server"
 OldValuesParameterFormatString="{0}" SelectMethod="GetProducts"
 TypeName="ProductsCL" UpdateMethod="UpdateProduct">
 <UpdateParameters>
 <asp:Parameter Name="productName" Type="String" />
 <asp:Parameter Name="unitPrice" Type="Decimal" />
 <asp:Parameter Name="productID" Type="Int32" />
 </UpdateParameters>
</asp:ObjectDataSource>

  要測試位於緩存層的SQL cache dependency,先在ProductCL類的AddCacheItem方法裡設置斷點(breakpoint),然後啟動調試。當你首次登錄SqlCacheDependencies.aspx頁面時,應該可以發生斷點,因為是第一次請求數據,且把數據添加到內存。然後,在GridView裡跳轉到下一頁或對某個列排序,這將導致GridView控件查詢所需的數據,數據應該還駐存在內存因為表Products沒有改動過。如果一直無法在內存找到所需的數據,務必確保內存夠大,然後再試一次。

  在GridView裡多跳轉幾頁,再另外打開一個浏覽器窗口,導航到Basics.aspx頁面(~/EditInsertDelete/Basics.aspx). 更新一條記錄。再回到第一個浏覽器窗口,再跳轉頁面或實施排序。

  此時,你會遇到下面2種情況之一:要麼程序發生斷點,提示你數據被清除了,原圖是數據庫發生了改動;要麼程序沒有發生斷點,這意味著頁面SqlCacheDependencies.aspx顯示的是“過時”的數據。如果沒有發生斷點,很可能是當數據改變時沒有觸發polling服務(polling service).我們知道,polling服務每隔設定的pollTime那麼多毫秒對Products表進行檢查,看是否改動過。因此在源數據的更新和“過時”數據的清除之間有個延遲期。

  注意:延遲很可能是當我們在SqlCacheDependencies.aspx頁面裡的GridView裡編輯產品信息時發生的。在教程《在分層架構中緩存數據》裡,我們添加MasterCacheKeyArray cache dependency來確保數據從內存清除。但在前面我們修改AddCacheItem方法時將其替換掉了,因此ProductsCL類將繼續顯示“過時”的數據,直到檢測系統發現Products發生過改動。我們將在第七步看如何重新引入MasterCacheKeyArray cache dependency.

第七步:對緩存條目附加多個Dependencies

  我們知道, MasterCacheKeyArray cache dependency的用處在於:與 product相關的所有條目中,只要其中任意一條的相關數據發生更改後,所有的條目都會被清除掉。舉個例,GetProductsByCategoryID(categoryID)方法根據指定的categoryID獲取多條產品記錄並緩存,只要其中任何一條記錄被清除掉的話, MasterCacheKeyArray cache dependency 會確保剩下的其它記錄也會被清除掉。

  沒有MasterCacheKeyArray cache dependency的話,就會存在這種可能性,當某個條目更改過後,剩余的條目仍然駐留在內存而顯得“過時”。因此,在使用SQL cache dependencies的時候包含MasterCacheKeyArray cache dependency是很重要的。然而,data cache的Insert 方法只允許存在一個dependency 對象。

  此外,當使用SQL cache dependencies的時候,我們可能要依賴多個表。比如,ProductsCL類的ProductsDataTable裡還包含了每個產品的種類(category)和供應商名稱(supplier names).但是在AddCacheItem方法裡,只依賴 表Products.設想,如果用戶更新了種類或供應商,那麼緩存的product數據仍然駐留在內存,顯然已經"過時"了。因此,我們想時緩存的product數據不僅依賴Products表,還要依賴Categories 和 Suppliers 表.

  不過類AggregateCacheDependency提供了這個途徑,將一個緩存條目與多個dependencies聯系起來。首先,創建一個AggregateCacheDependency實例;然後用AggregateCacheDependency的 Add 方法添加設置好的dependencies.當AggregateCacheDependency 實例裡的任何一個dependencies發生改動以後,緩存條目就會被清除掉。

  下面的代碼是更新過的ProductsCL類的 AddCacheItem 方法。該方法不僅創建了MasterCacheKeyArray cache dependency,還創建了基於表Products, Categories,和 Suppliers的多個SqlCacheDependency objects對象。再把它們組合起來構成一個名為aggregateDependencies的AggregateCacheDependency object 對象,將該對象傳遞給Insert方法.

private void AddCacheItem(string rawKey, object value)
{
 System.Web.Caching.Cache DataCache = HttpRuntime.Cache;

 // Make sure MasterCacheKeyArray[0] is in the cache and create a depedency
 DataCache[MasterCacheKeyArray[0]] = DateTime.Now;
 Caching.CacheDependency masterCacheKeyDependency =
 new Caching.CacheDependency(null, MasterCacheKeyArray);

 // Add the SqlCacheDependency objects for Products, Categories, and Suppliers
 Caching.SqlCacheDependency productsTableDependency =
 new Caching.SqlCacheDependency("NorthwindDB", "Products");
 Caching.SqlCacheDependency categoriesTableDependency =
 new Caching.SqlCacheDependency("NorthwindDB", "Categories");
 Caching.SqlCacheDependency suppliersTableDependency =
 new Caching.SqlCacheDependency("NorthwindDB", "Suppliers");

 // Create an AggregateCacheDependency
 Caching.AggregateCacheDependency aggregateDependencies =
 new Caching.AggregateCacheDependency();
 aggregateDependencies.Add(masterCacheKeyDependency, productsTableDependency,
 categoriesTableDependency, suppliersTableDependency);

 DataCache.Insert(GetCacheKey(rawKey), value, aggregateDependencies,
 Caching.Cache.NoAbsoluteExpiration, Caching.Cache.NoSlidingExpiration);
}

  對代碼進行測試。現在,更改表Products、 Categories, 或Suppliers的話將清除掉緩存數據。另外,當在GridView控件裡編輯某個產品的話將調用ProductsCL 類的 UpdateProduct方法,該方法清除掉 MasterCacheKeyArray cache dependency,進而導致連鎖反應清除掉緩存的ProductsDataTable.最後的結果是,當下次請求數據時將重新從數據庫檢索數據。
注意:也可以通過output caching來使用SQL cache dependencies.欲見詳情,請參考《Using ASP.NET Output Caching with SQL Server.》(http://msdn2.microsoft.com/en-us/library/e3w8402y(VS.80).aspx)

結語:

  當緩存數據庫數據時,最理想的狀態是數據一直駐留在內存,直到數據庫發生了改動。在ASP.NET 2.0,可以通過編程或聲明代碼的方式使用SQL cache dependencies ,該方法面臨的挑戰是及時檢測數據發生的改動。Microsoft SQL Server 2005 的完整版提供了notification功能,該功能向應用程序通告某個數據查詢返回的結果已經改變了。而對SQL Server 2005的Express版,以及更舊的版本而言,只有使用polling檢測系統了。不過還好,為polling設置必要的構造是很簡單的。

  祝編程快樂!

作者簡介

  本系列教程作者 Scott Mitchell,著有六本ASP/ASP.NET方面的書,是4GuysFromRolla.com的創始人,自1998年以來一直應用 微軟Web技術。大家可以點擊查看全部教程《[翻譯]Scott Mitchell 的ASP.NET 2.0數據教程》,希望對大家的學習ASP.NET有所幫助。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved