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
--------
沒什麼特別的~
不過是一些筆記而已