您好,登錄后才能下訂單哦!
ASP.NET 2.0中怎么利用ObjectDataSource緩存數據,很多新手對此不是很清楚,為了幫助大家解決這個難題,下面小編將為大家詳細講解,有這方面需求的人可以來學習下,希望你能有所收獲。
主要的緩存要點
由于緩存通過將數據的副本放置在一個便于快速訪問的地方來提高程序的總體性能。由于它僅僅是一個副本,當源數據發生改變時,副本不能同步更新。為此,頁面開發員應制定一個標準將其清除出內存,可以使用如下的2種方法之一:
Time-based標準:向內存添加的條目(item),只能在內存里駐留固定或靈活(sliding)的一段時間。比如,開發者可設定一個時間段,比如60秒,當條目添加到內存后,不管訪問它的頻率有多高,60秒后就會被清除掉;如果是靈活(sliding)處理的話,當最后一次被訪問后,未再次被訪問的時間一旦超出60秒,也會被清除掉。
Dependency-based標準:當向內存添加條目時為其分配一個從屬體(dependency),當條目對應的從屬體發生改變時將條目清除掉。從屬體可以是一個文件;另一個緩存條目;或者干脆是這兩者的混合體( combination);當然還可以是SQL cache dependencies,它可以向內存添加條目,當源數據改變時將條目清除掉。我們將在接下來的文章《使用SQL緩存依賴項SqlCacheDependency 》里詳細考察。
不管是哪種標準,在條目被清除掉以前,我們都可以對其訪問。如果內存達到了它的極限,它會清除掉已有的條目后再添加新的條目。因此,當處理緩存數據時很重要的一點是我們要充分考慮到緩存數據已被清除的可能。在下一篇文章《在分層架構中緩存數據》我們考察采用哪種模式從內存訪問數據。
緩存是提升程序性能的一種較為經濟的方法,就像Steven Smith在他的文章《ASP.NET Caching: Techniques and Best Practices:》里闡述的一樣:“緩存是獲得‘上佳'性能的一種好方法,不需要太多的時間和分析。… 存儲器也便宜,要獲得你期望的性能,靠緩存技術你需要花30秒;靠優化代碼和數據庫你可能要幾天乃至幾周時間…”
雖然緩存可以顯而易見的提升系統性能,但并不是適用于所有的應用程序,比如某些實時(real-time)、頻繁更新數據的程序就不適合。
但是對大部分程序而言,還是適用的。關于ASP.NET 2.0里的緩存的更多背景資料請參考ASP.NET 2.0 QuickStart Tutorials系列的Caching for Performance 部分。
第一步:創建Caching頁面
在我們開始以前,首先讓我們花些時間來添加包括本篇在內的最近四篇教程需要用到的頁面。我們先在項目中新建一個稱作Caching的文件夾,接下來,為目錄新增以下幾個頁面,并配置為使用Site.master母板頁。
Default.aspx
ObjectDataSource.aspx
FromTheArchitecture.aspx
AtApplicationStartup.aspx
SqlCacheDependencies.aspx
圖1:創建相關的ASP.NET頁面
像其它文件夾一樣,Caching文件夾里的Default.aspx頁面將本系列的文章顯示出來。記得用戶控件SectionLevelTutorialListing.ascx提供該功能,設計模式里將其拖到頁面上。
圖2:為Default.aspx頁面添加用戶控件SectionLevelTutorialListing.ascx
最后,將這些頁面添加到Web.sitemap文件里,特別的,放在“Working with Binary Data” <siteMapNode>:之后:
<siteMapNode title="Caching" url="~/Caching/Default.aspx" description="Learn how to use the caching features of ASP.NET 2.0."> <siteMapNode url="~/Caching/ObjectDataSource.aspx" title="ObjectDataSource Caching" description="Explore how to cache data directly from the ObjectDataSource control." /> <siteMapNode url="~/Caching/FromTheArchitecture.aspx" title="Caching in the Architecture" description="See how to cache data from within the architecture." /> <siteMapNode url="~/Caching/AtApplicationStartup.aspx" title="Caching Data at Application Startup" description="Learn how to cache expensive or infrequently-changing queries at the start of the application." /> <siteMapNode url="~/Caching/SqlCacheDependencies.aspx" title="Using SQL Cache Dependencies" description="Examine how to have data automatically expire from the cache when its underlying database data is modified." /> </siteMapNode>
完成Web.sitemap文件的更新后,讓我們在瀏覽器里查看,左邊的菜單欄顯示caching章節的文章
圖3:網站地圖Site Map包含了Caching章節的文章
第二步:在Web Page頁面里展示產品
本文考察怎樣使用ObjectDataSource控件內置(built-in)的緩存功能。在開始之前,我們首先需要創建一個頁面,用一個ObjectDataSource控件調用ProductsBLL class類獲取產品信息,再用GridView控件展示出來。
首先打開Caching文件夾里的ObjectDataSource.aspx頁面。從工具箱拖一個GridView控件到頁面,設置其ID為Products,再從智能標簽里選擇將其綁定到一個ObjectDataSource控件,ID為ProductsDataSource。設該ObjectDataSource使用ProductsBLL class類。
圖4:設置ObjectDataSource控件使用ProductsBLL Class類
在本頁面,我們要創建一個允許編輯的GridView控件,當ObjectDataSource控件里的緩存數據發生改變時,我們可以通過GridView的界面查看到底會發生什么。在SELECT標簽里選擇默認的GetProducts()方法, 但是在UPDATE標簽里選擇接受productName, unitPrice 和productID作為輸入參數的UpdateProduct()重載方法。
圖5:在UPDATE標簽里選擇重載的UpdateProduct()方法
最后,在INSERT和DELETE標簽里選擇“(None)”,點完成按鈕。一旦完成“設置數據源向導”,Visual Studio會將ObjectDataSource控件的OldValuesParameterFormatString屬性設置為original_{0}。就像在前面的教程之16章《概述插入、更新和刪除數據》里探討的一樣,該屬性要么刪除掉,要么設置為{0},不然的話更新操作會報錯。
此外,完成向導后,Visual Studio會將產品的所有數據列添加到GridView控件,將除了ProductName, CategoryName和UnitPrice之外的所有綁定列(BoundFields)刪除。然后,分別將上述3列的HeaderText屬性改為Product”, “Category”和“Price”。由于ProductName是必需的,將ProductName列轉變成模板列(TemplateField),在EditItemTemplate里添加一個RequiredFieldValidator控件;同樣的,將UnitPrice列也轉換成模板列,并添加一個CompareValidator控件,確保用戶輸入的是大于或等于0的有效的貨幣值。除此以外,你還可以作一些界面上的改進,比如使UnitPrice值居中,或分別對UnitPrice的只讀和編輯界面作一些格式化的處理。
在GridView的智能標簽里點相關項啟動編輯、分頁、排序功能。
注意:想回顧怎樣自定義GridView的編輯界面嗎?請參考前面的文章之20《定制數據修改界面》
圖6:啟用GridView的編輯、排序、分頁功能。
完成GridView的修改后,GridView 和 ObjectDataSource的代碼聲明看起來像下面這樣:
<asp:GridView ID="Products" runat="server" AutoGenerateColumns="False" DataKeyNames="ProductID" DataSourceID="ProductsDataSource" 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:TextBox> <asp:RequiredFieldValidator ID="RequiredFieldValidator1" Display="Dynamic" ControlToValidate="ProductName" SetFocusOnError="True" ErrorMessage="You must provide a name for the product." runat="server">*</asp:RequiredFieldValidator> </EditItemTemplate> <ItemTemplate> <asp:Label ID="Label2" runat="server" Text='<%# Bind("ProductName") %>'></asp:Label> </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" 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" runat="server" 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="ProductsDataSource" runat="server" OldValuesParameterFormatString="{0}" 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>
如圖7所示,GridView列出了每個產品的name, category和price信息。花幾分鐘測試頁面—對結果排序,查看分頁,編輯某條記錄。
圖7:顯示每條記錄的Name, Category和Price信息
第三步:考察ObjectDataSource如何請求數據
ID為Products的GridView通過調用名為ProductsDataSource的ObjectDataSource的Select()方法檢索數據并將它顯示出來。該ObjectDataSource創建業務邏輯層的ProductsBLL class類的一個實例并調用它的GetProducts()方法,該方法又調用數據訪問層ProductsTableAdapter的GetProducts()方法。數據訪問層連接到數據庫Northwind,并執行已設置好了的SELECT查詢。查詢數據以NorthwindDataTable的形式返回到數據訪問層,該DataTable對象再依次傳回到業務邏輯層,ObjectDataSource、GridView控件。GridView控件為DataTable里的每一數據行(DataRow)創建一個GridViewRow對象,每個GridViewRow對象最終被編譯為HTML返回到客戶端,呈現在訪問者的瀏覽器里。
任何時候,當GridView控件需要綁定時,按上述的事件發生順序執行。比如,首次登錄頁面;將數據從一個頁面傳遞到另一個頁面;在GridView里排序;通過GridView內建的編輯或刪除界面改動數據。當GridView的視圖(view sta被設為disabled時,每次頁面回傳時也會對GridView重新綁定;當然我們可以顯式地調用DataBind()方法來對GridView實施綁定。
為了更清除地揭示從數據庫檢索數據的頻率,我們顯示一個消息,提示在某時程序在檢索數據。為此,在GridView控件上添加一個ID為ODSEvents的Label控件,清除其Text屬性,將其EnableViewState屬性設置為false。在Label控件下面再添加一個Button控件,設其Text屬性為“Postback”.
圖8:在GridView上添加Label 和 Button控件
在整個數據檢索過程中,首先觸發ObjectDataSource的Selecting事件,并調用其對應的已設置好的方法。為該事件創建一個事件處理器,添加如下的代碼:
protected void ProductsDataSource_Selecting(object sender, ObjectDataSourceSelectingEventArgs e) { ODSEvents.Text = "-- Selecting event fired"; }
每當ObjectDataSource開始檢索數據時,Label控件都會顯示文本“Selecting event fired”.
在瀏覽器訪問該頁面。當首次登錄時,文本“Selecting event fired”就會顯示出來。點“Postback”按鈕時,我們注意到文本消失了(前提是你將GridView的EnableViewState屬性設置為默認值true)。這是因為當頁面回傳時,GridView通過它的視圖狀態(view state)載入數據進行重建(reconstructed),因此不再需要通過ObjectDataSource檢索數據庫來得到數據進行重建。然而,排序、分頁、編輯等都會促使GridView重新綁定到數據源,因此,文本“Selecting event fired”又出現了。
圖9:當GridView重新綁定到數據源時,顯示文本“Selecting event fired”
圖10:點“Postback” 按鈕導致GridView從視圖狀態“View State”獲取數據
每次分頁、排序時都需要從數據庫檢索數據,這看起來有點浪費資源。即便GridView不支持排序和分頁,任何人每次第一次登錄頁面時都需要從數據庫檢索數據(如果將view state設置為disabled的話,每次頁面回轉也會檢索數據)。如果GridView對所有用戶顯示的數據都是一樣話,那么額外的數據庫查詢是浪費。我們可以對GetProducts()方法返回的數據進行緩存,再將GridView綁定到這些緩存數據。
第四步:用ObjectDataSource緩存數據
僅僅簡單的設置某些屬性,我們就可以讓ObjectDataSource對它的檢索數據自動的進行緩存。以下總結了ObjectDataSource控件的與緩存相關的屬性:
EnableCaching—必須設置為true,默認為false.
CacheDuration—緩存時間,以秒為單位。默認為0,只有當EnableCaching屬性設置為true,且CacheDuration設為大于0的值時ObjectDataSource控件才會緩存數據。
CacheExpirationPolicy—可設置為Absolute 或 Sliding。如果為Absolute,當它設為多少秒時,ObjectDataSource就會對檢索的數據緩存多少秒;如果為Sliding,當它設為多少秒時,一旦超過那么多秒沒有對緩存數據進行訪問,就終止緩存。默認為Absolute。
CacheKeyDependency—用該屬性將ObjectDataSource的緩存條目(entry)與現有的緩存從屬體關聯起來。利用可以它將緩存條目提前從內存清除掉。絕大多數情況下用該屬性把SQL cache dependency與ObjectDataSource的緩存關聯起來。這個話題我們將在后面的教程《使用SQL緩存依賴項SqlCacheDependency 》考察。
讓我們設置ID為ProductsDataSource的ObjectDataSource 的數據緩存時間為30秒。設其EnableCaching屬性為true;設其CacheDuration屬性為30;CacheExpirationPolicy屬性為默認的Absolute。
圖11:設置ObjectDataSource的緩存時間為30秒
保存你的設置,并在瀏覽器里查看。當你第一次登錄頁面時,文本“Selecting event fired”會顯示出來,因為原始數據還未緩存。但你點“Postback”按鈕,或進行分頁,排序,或點編輯、取消按鈕時,文本“Selecting event fired”就不會顯示出來了。原因是只有當ObjectDataSource控件檢索數據時才會觸發Selecting事件;如果ObjectDataSource控件是從緩存里面獲取數據的話就不會觸發Selecting事件。
過了30秒后,數據將從內存清除;或者調用ObjectDataSource控件的Insert, Update,或Delete方法的話數據也會被清除掉。因此,過了30秒后或點擊“Update”按鈕,編輯,取消按鈕,或排序、分頁的話就會促使ObjectDataSource檢索數據,觸發Selecting事件,文本“Selecting event fired”又會顯示出來。最后,再對檢索得到的數據進行緩存。
注意:如果你看到文本“Selecting event fired”頻繁的出現,很可能是內存容量太小。如果沒有足夠的容量,ObjectDataSource添加到內存的數據可能被清除掉了。如果ObjectDataSource沒有或者只是偶爾地對數據緩存,請關閉一些應用程序來釋放掉內存,然后再試一次。
圖12揭示了ObjectDataSource的緩存流程。當文本“Selecting event fired”出現在屏幕上時,那是因為數據沒有在緩存里找到,必須進行相關檢索。當文本消失時,那是因為數據進行了緩存。當從緩存得到了所需的數據時,沒有任何數據查詢執行。
圖12:ObjectDataSource在Data Cache里存儲和獲取數據
每一個ASP.NET應用程序有它自己的數據緩存實例,所有的頁面和用戶都可以進行訪問。那意味著對于ObjectDataSource控件緩存的數據,所有登錄該頁面的用戶都可以訪問。來做個驗證,在一個瀏覽器里打開ObjectDataSource.aspx頁面,當第一次登錄該頁面時,文本“Selecting event fired”顯現出來(假定前面測試時緩存的數據到此時已經被清除掉了)。再開第二個瀏覽器,將第一個瀏覽器里的URL地址拷貝、粘貼過來。在第二個瀏覽器里,文本“Selecting event fired”并沒有顯示出來,因為它使用的是第一個瀏覽器頁面緩存的數據。
當向內存添加檢索數據時,ObjectDataSource要用到一個叫cache key的值,該值包括:CacheDuration 和 CacheExpirationPolicy屬性的值;ObjectDataSource調用的業務對象的類型(type),它由TypeName 屬性指定(比如:ProductsBLL);SelectMethod 屬性的值,以及SelectParameters參數集里參數的name 和 values;StartRowIndex 和 MaximumRows屬性的值,它用來執行用戶自定義分頁(custom paging)。
將這些屬性值組合在一起構成cache key值是為了對每一個緩存條目提供唯一的標識值。比如,在前面的教程里,我們使用ProductsBLL類的GetProductsByCategoryID(categoryID)方法來獲取某個指定類的所有產品。假如一個用戶在頁面查看飲料類(其CategoryID值為1)的產品信息,如果ObjectDataSource控件在進行數據緩存時忽略SelectParameters的值,當另一個用戶登錄頁面查看調味品類的產品信息時,恰好飲料類產品信息正好緩存在內存里,第二個用戶將會看到飲料類的產品信息,而非他想要的調味品類的產品信息。所以,當cache key值包含electParameters的值的話,ObjectDataSource緩存數據的時候就可以將調味品類和飲料類區分開來。
數據更新不同步(Stale Data)問題
當調用ObjectDataSource控件的Insert, Update和 Delete其中一個方法時,它都會將緩存條目從內存清除掉。這樣做的好處在于當從頁面修改數據時將緩存的舊數據清除掉。然而,ObjectDataSource還是可能有將“未更新數據”(也就是源數據已經發生改變,而緩存的數據沒同步更新)顯示出來的情況。最簡單的例子是直接從數據庫修改數據,比如某個數據庫管理員運行一個腳本,修改數據庫里的某些記錄。
在此,我們探討一種微妙的情況。雖然調用ObjectDataSource的數據修改方法時它會將緩存數據清除掉,但清除的是那些與ObjectDataSource的“屬性組合值”(combination of property)相匹配的緩存條目(比如CacheDuration, TypeName, SelectMethod等)。假如你有2個ObjectDataSources控件,它們更新相同的數據,當使用不同的SelectMethods 或 SelectParameters,當第一個ObjectDataSources控件更新某一行記錄而清除該行對應的緩存數據時,第二個ObjectDataSources控件仍然使用該行對應的緩存數據。我們來做個驗證,創建一個頁面,包含可編輯的GridView控件,其對應的ObjectDataSource控件設置為使用緩存,且調用ProductsBLL類的GetProducts()方法來獲取數據。在本頁(或另外創建一個頁面)再添加GridView 和ObjectDataSource控件,但是設置第二個ObjectDataSource控件調用GetProductsByCategoryID(categoryID)方法。由于這2個ObjectDataSource控件的SelectMethod屬性不同,因此它們各自有各自不同的緩存值。如果你在第一個GridView控件里編輯某個產品,然后在第二個GridView控件里重新綁定數據(比如分頁、排序等),我們在第二個GridView控件里看到該產品的值依然是“老的緩存數據”(而并不是第一個GridView控件修改后的值)
簡而言之,如果你樂于使用“老的緩存數據”,那只有使用基于時間的緩存時間值(time-based expiries,也就是設置具體的緩存時間長度),如果對數據及時更新要求很高的話,將緩存時間設短點。如果不允許使用“老的緩存數據”的話,要么放棄緩存,要么使用SQL cache dependencies(你可以認為它是你緩存的數據庫數據)。我們將在后面探討SQL cache dependencies。
看完上述內容是否對您有幫助呢?如果還想對相關知識有進一步的了解或閱讀更多相關文章,請關注億速云行業資訊頻道,感謝您對億速云的支持。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。