CacheDependency 應用以及 Cache 過期時間的最低限制
遞送電子採購單、領料單等等和物料有關的表單時,通常都必須檢核請領產品是否為特殊管制物品,並做相對應的處理,但是產品有數十萬項,因此不可能快取所有產品,但是若使用者在同一次上線作業中,遞送表單有上百筆,但是裡面有許多重複的品項,每一筆都連線到資料庫去檢查是否為管制物品,根本就意圖謀殺資料庫,所以較好的方式就是做短時間的產品資料快取,而且同時設定快取的未使用過期時間和絕對過期時間,節省伺服器資源。
頁面放計時器,為了方便測試和顯示快取資料:
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title></title> </head> <body> <form id="form1" runat="server"> <div> <asp:ScriptManager ID="ScriptManager1" runat="server"> </asp:ScriptManager> <asp:Timer ID="Timer1" runat="server" Interval="1000" ontick="Timer1_Tick"> </asp:Timer> <asp:UpdatePanel ID="UpdatePanel1" runat="server" UpdateMode="Conditional"> <ContentTemplate> 現在時間:<asp:Label ID="lblTime" runat="server"></asp:Label> </ContentTemplate> <Triggers> <asp:AsyncPostBackTrigger ControlID="Timer1" EventName="Tick" /> </Triggers> </asp:UpdatePanel> <p> ProductFlag:<asp:Label ID="lblProp" runat="server"></asp:Label> <asp:Button ID="btnRefresh" runat="server" Text="取得ProductFlag" onclick="btnRefresh_Click" /> </p> Cache更新時間:<asp:Label ID="lblCacheTime" runat="server"></asp:Label> </div> </form> </body> </html>
程式碼:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Caching; using System.Web.UI; using System.Web.UI.WebControls; public partial class _Default : System.Web.UI.Page { //假裝是資料庫查回來的值 private static bool _ProductFlag = false; protected void Page_Load(object sender, EventArgs e) { if (!IsPostBack) { lblProp.Text = ShowProductFlag("假的ID").ToString(); } } protected bool ShowProductFlag(string ID) { Dictionary<string, bool> MaterialFlag; //初始化快取資料 MaterialFlag = Cache.Get("ProductFlagCache") == null ? new Dictionary<string, bool>() : (Dictionary<string, bool>)Cache.Get("ProductFlagCache"); //若ID不存在於快取中,就去資料庫查回來,並更新快取資料 if (MaterialFlag.ContainsKey(ID) == false) { //快取的絕對過期時間,因為不能同時設定絕對過期和相對過期時間, //所以要再透過CacheDependency做快取相依性設定 //請注意,同時設定絕對過期和相對過期,編譯不會有錯,執行時間才會死掉 MaterialFlag.Add(ID, GetProductFlag(ID)); Cache.Insert("ExpireFlag", DateTime.Now, null, DateTime.Now.AddSeconds(40), Cache.NoSlidingExpiration); //透過CacheDependency設定要相依的快取索引名稱(可以很多個,所以是陣列) CacheDependency cd = new CacheDependency(null, new string[] { "ExpireFlag" }); Cache.Insert("ProductFlagCache", MaterialFlag, cd, Cache.NoAbsoluteExpiration, TimeSpan.FromSeconds(10)); lblCacheTime.Text = DateTime.Now.ToLongTimeString(); } return MaterialFlag[ID]; } /// <summary> /// 假裝回資料庫查,每次查詢後就把值給換掉。 /// </summary> /// <param name="ID">The ID.</param> protected bool GetProductFlag(string ID) { bool result = _ProductFlag; _ProductFlag = !_ProductFlag; return result; } protected void Timer1_Tick(object sender, EventArgs e) { lblTime.Text = DateTime.Now.ToLongTimeString(); } protected void btnRefresh_Click(object sender, EventArgs e) { lblProp.Text = ShowProductFlag("假的ID").ToString(); } }
範例畫面:
同場加映:一開始測試時,我為了快速測試,就把絕對過期時間設定為 10 秒,結果發現要到 20 秒才會過期重新寫快取資料,改成 5 秒、15 秒,也都一樣要 20 秒才會過期,百思不得其解,程式也都正確,後來終於查到,這是 .Net Fremework 寫死在 CacheExpires 的建構式中:
_tsPerBucket = new TimeSpan(0, 0, 20);
而且還是唯讀……換言之,絕對過期時間請設定 20 秒的倍數吧,而且不能低於 20 秒,因為它每 20 秒才會檢查有無過期。此問題確認原因後,我沒繼續查怎麼解決,一則是 20 秒我可以接受,一則是:我想睡了 zzz ZZZ
--------
沒什麼特別的~
不過是一些筆記而已