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

C# RealProxy 實戰:透過 AOP 實現方法級緩存 (Cachable)

     編輯:關於C#

背景

利用 RealProxy 攔截方法調用,配合 Attribute 標記,實現聲明式緩存:命中緩存直接返回,未命中執行方法並存入緩存。類似 MVC Action 執行管線。

架構圖解

  • Code Consumer:調用方
  • ProxyAttribute:關聯自定義 Proxy 與目標類
  • CachableRealProxy:繼承 RealProxy,實現 Invoke、前置/後置處理
  • CachableAttribute:標記方法,指定緩存 Key、位置、過期策略

核心組件

1. 輔助類

public class FollowAction {
    public bool StopExecute { get; set; } // 前置攔截:true 表示跳過原方法
    public object Result { get; set; }    // 緩存值
}

2. CachableRealProxy

public abstract class CachableRealProxy : RealProxy {
    private readonly MarshalByRefObject _target;
    protected CachableRealProxy(Type type, MarshalByRefObject target) : base(type) => _target = target;

    public override IMessage Invoke(IMessage msg) {
        var call = (IMethodCallMessage)msg;
        var attrs = call.MethodBase.GetCustomAttributes(typeof(CachableAttribute), false);
        bool hasCacheAttr = attrs.Length > 0;

        FollowAction follow = null;
        if (hasCacheAttr) follow = PreProcess(msg);

        object retVal;
        if (follow != null && follow.StopExecute) retVal = follow.Result;
        else retVal = call.MethodBase.Invoke(_target, call.Args);

        var retMsg = new ReturnMessage(retVal, call.Args, call.Args.Length, call.LogicalCallContext, call);

        if (follow == null || !follow.StopExecute)
            if (hasCacheAttr) PostProcess(msg, retMsg);

        return retMsg;
    }

    protected virtual FollowAction PreProcess(IMessage msg) {
        var attr = GetCacheAttribute(msg);
        if (attr == null) return null;
        var key = attr.GenerateCacheKey();
        var cached = CacheManager.Instance().GetCacheCore().Get(attr.Location, key);
        if (cached != null) return new FollowAction { StopExecute = true, Result = cached };
        return null;
    }

    protected virtual void PostProcess(IMessage msg, IMessage retMsg) {
        var attr = GetCacheAttribute(msg);
        if (attr == null) return;
        var key = attr.GenerateCacheKey();
        CacheManager.Instance().GetCacheCore().Set(attr.Location, key, ((ReturnMessage)retMsg).ReturnValue);
    }

    private CachableAttribute GetCacheAttribute(IMessage msg) {
        var call = (IMethodCallMessage)msg;
        return call.MethodBase.GetCustomAttributes(typeof(CachableAttribute), false).FirstOrDefault() as CachableAttribute;
    }
}

3. Attribute 定義

[AttributeUsage(AttributeTargets.Method)]
public class CachableAttribute : Attribute {
    public string Location { get; set; }
    public string Key { get; set; }
    public TimeSpan ExpireTimeSpan { get; set; }
    public string GenerateCacheKey() => Key; // 可擴展支援參數插值
}

public class CachableEnabledAttribute : ProxyAttribute {
    public override MarshalByRefObject CreateInstance(Type serverType) {
        var target = base.CreateInstance(serverType);
        return (MarshalByRefObject)new CachableRealProxy(serverType, target).GetTransparentProxy();
    }
}

4. 目標類

[CachableEnabled]
public class RealObject : ContextBoundObject {
    [Cachable(Location = "OrdersQuery", Key = "OrderQueryService_QueryByFirstName_{0}")]
    public override QueryResult QueryByFirstName(string firstName, PagingInfo pgInfo) {
        // 模擬耗時查詢
        return new QueryResult { /* ... */ };
    }
}

測試與效果

var srv = new RealObject();
while (true) {
    Thread.Sleep(1000);
    var result = srv.QueryByFirstName("aaron", new PagingInfo());
    // 首次查詢耗時,後續 1 秒內直接返回緩存,時間戳不變
}

控制台輸出顯示緩存命中後返回結果時間不再更新。

關鍵點總結

  • 目標類必須繼承 ContextBoundObject
  • RealProxy 攔截所有虛擬方法調用
  • 前置處理返回 FollowAction.StopExecute=true 可跳過原方法
  • 緩存 Key 生成可擴展支援方法參數動態插值
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved