首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> C# >> 關於C#

C# 匿名函數與快取模式完美結合:打造通用、可串聯、支援多策略的 CacheManager

     編輯:關於C#

設計目標

將「資料來源取得邏輯」以 Lambda 表達式注入快取元件,實現:

  • 宣告式定義:一行程式碼完成快取實例建立。
  • 策略模式:支援 SimpleCache (記憶體)、TimeoutCache (過期淘汰)、DatabaseCache (資料庫變更通知) 等多種儲存後端。
  • 串聯快取:CacheManager 實作 ICache,可疊加多層 (L1 記憶體 → L2 Redis → L3 DB)。

介面定義

public interface ICache {
    T Get(object key);
    void Set(object key, T value);
}

public interface ICacheManager : ICache {
    Func SourceGetter { get; } // 資料來源工廠
}

三種快取策略實作

1. SimpleCache - 簡單字典

public class SimpleCache : ICache {
    readonly Dictionary _store = new();
    public T Get(object key) => _store.TryGetValue(key, out var v) ? v : default;
    public void Set(object key, T value) => _store[key] = value;
}

2. TimeoutCache - 定期掃描過期項目

public class TimeoutCache : ICache {
    public TimeSpan Expire { get; set; } = TimeSpan.FromMinutes(5);
    readonly Dictionary> _store = new();
    DateTime _lastScan = DateTime.Now;

    public T Get(object key) {
        TryScanExpired();
        if (!_store.TryGetValue(key, out var kv)) return default;
        if (kv.Value.Add(Expire) < DateTime.Now) {
            _store.Remove(key);
            return default;
        }
        return kv.Key;
    }

    public void Set(object key, T value) => _store[key] = new KeyValuePair(value, DateTime.Now);

    void TryScanExpired() {
        if ((DateTime.Now - _lastScan).TotalSeconds > Expire.TotalSeconds / 2) {
            _lastScan = DateTime.Now;
            foreach (var k in _store.Where(kv => kv.Value.Value.Add(Expire) < DateTime.Now).Select(kv => kv.Key).ToList())
                _store.Remove(k);
        }
    }
}

3. DatabaseCache - 繫結資料庫 Context 生命週期

public class DatabaseCache : ICache {
    readonly Dictionary _store = new();
    public DatabaseCache() {
        // 假設 SmsDatabase.Instance 為單例 Context
        SmsDatabase.Instance.PreDisposing += (s, e) => _store.Clear();
    }
    public T Get(object key) => _store.TryGetValue(key, out var v) ? v : default;
    public void Set(object key, T value) => _store[key] = value;
}

核心 CacheManager - 串聯入口

public class CacheManager : ICacheManager {
    readonly ICache _innerCache;
    public Func SourceGetter { get; }

    public CacheManager(ICache innerCache, Func sourceGetter) {
        _innerCache = innerCache;
        SourceGetter = sourceGetter;
    }

    public T Get(object key) {
        var data = _innerCache.Get(key);
        if (data != null) return data; // 快取命中

        data = SourceGetter(key);      // 來源取得 (Lambda 執行)
        _innerCache.Set(key, data);    // 回填快取
        return data;
    }

    public void Set(object key, T value) => _innerCache.Set(key, value);
}

實際宣告與使用

// 單層快取:記憶體 + 過期策略 + DB 來源
static readonly CacheManager CompanyCache = new CacheManager(
    new TimeoutCache { Expire = TimeSpan.FromMinutes(10) },
    key => DbContext.Current.Companies.FirstOrDefault(c => c.Code == (string)key)
);

// 串聯快取:L1 記憶體 (1分鐘) → L2 資料庫通知快取 → 真實 DB
static readonly CacheManager CompanyCacheMulti = new CacheManager(
    new TimeoutCache { Expire = TimeSpan.FromMinutes(1) },
    key => {
        // L2 快取同樣是 CacheManager,形成遞迴串聯
        return L2Cache.Get(key); 
    }
);

// 使用
var company = CompanyCache.Get("COMP001"); // 自動處理 Miss → Load → Cache
CompanyCache.Set("COMP001", updatedCompany); // 手動更新/失效

進階擴充建議