傳統三層架構常將唯讀/可修改唯讀資料表(如產品分類、稱謂代碼)快取於中間層。問題在於:資料庫異動時,中間層快取如何及時得知?
SQL Server 2005 + ADO.NET 2.0 內建 查詢通知 機制,徹底解決此問題。

SqlCommand 附加 SqlDependency,送出 TDS 封包含通知請求。msdb 內建服務 QueryNotificationService。sp_DispatcherProc (CLR) 讀取佇列,透過 HTTP/TCP 推送通知給用戶端。SqlDependency.OnChanged 事件觸發,應用程式重新查詢並刷新快取。兩部分命名、明確列出欄位、無 OUTER JOIN、無彙總函數 等。SELECT *、临时表、不確定性函數 (如 GETDATE())。ALTER DATABASE SET ENABLE_BROKER。SUBSCRIBE QUERY NOTIFICATIONS 權限。using (SqlConnection conn = new SqlConnection(connStr))
using (SqlCommand cmd = new SqlCommand("SELECT Au_ID, Au_LName FROM dbo.Authors", conn))
{
SqlDependency dep = new SqlDependency(cmd);
dep.OnChanged += OnDataChanged;
conn.Open();
using (SqlDataReader dr = cmd.ExecuteReader()) { /* 處理資料 */ }
// 連線可關閉,通知仍會送達
Console.ReadLine(); // 等待通知
}
void OnDataChanged(object sender, SqlNotificationEventArgs e)
{
Console.WriteLine($"資料變更:Type={e.Type}, Info={e.Info}, Source={e.Source}");
// 重新查詢並刷新快取
RefreshCache();
}
SqlNotificationRequest req = new SqlNotificationRequest();
req.Id = Guid.NewGuid();
req.Service = "MyCustomService"; // 需預先建立 QUEUE/SERVICE
req.Timeout = 0;
cmd.Notification = req;
// 執行查詢...
// 另一執行緒/程式輪詢佇列
using (SqlCommand waitCmd = new SqlCommand("WAITFOR(RECEIVE CONVERT(XML, message_body) FROM MyQueue)", conn))
{
object msg = waitCmd.ExecuteScalar(); // 封鎖直到收到通知
// 解析 XML 訊息自行處理
}
// 程式碼方式
SqlCacheDependency dep = new SqlCacheDependency(cmd);
Response.Cache.SetExpires(DateTime.Now.AddSeconds(60));
Response.AddCacheDependency(dep);
// 聲明式:頁面指示詞
// <%@ OutputCache SqlDependency="CommandNotification" Duration="60" VaryByParam="none" %>
支援 SQL Server 2000/2005(舊版透過輪詢觸發器表實作)。
SqlNotificationEventArgs 三屬性:
| 屬性 | 常見值 | 意義 |
|---|---|---|
| Type | Change, Subscribe, Fire | 通知類型 |
| Source | Data, Database, Statement | 來源 |
| Info | Insert, Update, Delete, Invalid, Timeout | 觸發原因 |
XML 訊息範例:
<qn:QueryNotification id="2" type="change" source="data" info="update"
database_id="6" user_id="1" xmlns:qn="...">
<qn:Message>{GUID}</qn:Message>
</qn:QueryNotification>
sys.dm_qn_subscriptions 動態管理檢視,DBA 可清理殘留訂閱:KILL QUERY NOTIFICATION SUBSCRIPTION <id>。Timeout 避免永久佔用資源。