利用 RealProxy 攔截方法調用,配合 Attribute 標記,實現聲明式緩存:命中緩存直接返回,未命中執行方法並存入緩存。類似 MVC Action 執行管線。
RealProxy,實現 Invoke、前置/後置處理public class FollowAction {
public bool StopExecute { get; set; } // 前置攔截:true 表示跳過原方法
public object Result { get; set; } // 緩存值
}
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;
}
}
[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();
}
}
[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 秒內直接返回緩存,時間戳不變
}
控制台輸出顯示緩存命中後返回結果時間不再更新。
ContextBoundObjectRealProxy 攔截所有虛擬方法調用FollowAction.StopExecute=true 可跳過原方法