LazyLoadableSink 是延遲載入的執行核心,攔截 get_、FieldGetter、GetValue 呼叫,判斷目標屬性是否為子實體集合、參考實體、參考屬性,若值為 null 則觸發資料庫查詢並快取結果。
internal class LazyLoadableSink : IMessageSink
{
private readonly IMessageSink _nextSink;
private readonly MarshalByRefObject _target;
private static readonly object SyncRoot = new object();
public LazyLoadableSink(MarshalByRefObject target, IMessageSink nextSink)
{
lock (SyncRoot)
{
_target = target;
_nextSink = nextSink;
}
}
public IMessage SyncProcessMessage(IMessage msg)
{
IMethodReturnMessage ret;
HandleMessage(msg, false, out ret);
return ret;
}
public IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink)
{
IMethodReturnMessage ret;
HandleMessage(msg, true, out ret);
return _nextSink.AsyncProcessMessage(msg, replySink);
}
public IMessageSink NextSink => _nextSink;
private void HandleMessage(IMessage msg, bool isAsync, out IMethodReturnMessage returnedMessage)
{
returnedMessage = null;
if (!isAsync && msg is IMethodCallMessage mcm)
{
Type entityType = _target.GetType();
bool isFill = (bool)entityType
.GetProperty("InnerIsFill", BindingFlags.Instance | BindingFlags.NonPublic)
.GetValue(_target, null);
// 判斷是否為屬性 get / FieldGetter / GetValue
bool isProperty = !isFill &&
((mcm.MethodName.Length > 4 && mcm.MethodName.StartsWith("get_")
&& mcm.MethodName != "get_InnerIsFill"
&& mcm.MethodName != "get_InnerDataState")
|| mcm.MethodName == "FieldGetter"
|| mcm.MethodName == "GetValue");
object oldValue = null;
MemberInfo member = null;
string propName = string.Empty;
if (isProperty)
{
// 解析屬性名
if (mcm.MethodName == "FieldGetter")
propName = mcm.InArgs[1].ToString();
else if (mcm.MethodName == "GetValue")
propName = mcm.InArgs[0].ToString();
else
propName = mcm.MethodName.Substring(4); // 移除 "get_"
member = Utility.GetMember(entityType, propName);
if (member == null) isProperty = false;
// 僅處理標記為導航屬性的成員
else if (!member.IsDefined(typeof(SubEntityListAttribute), true)
&& !member.IsDefined(typeof(ReferenceEntityAttribute), true)
&& !member.IsDefined(typeof(ReferencePropertyAttribute), true))
isProperty = false;
if (isProperty)
oldValue = Utility.GetMemberValue(_target, member, propName);
}
// 核心:值為 null 時才觸發延遲載入
if (isProperty && oldValue == null)
{
// 從快取取得資料操作上下文
string key = DataCacheKeyManager.GetEntityInnerData(entityType);
object innerData = InnerCache<object[]>.Get(key);
if (innerData != null)
{
object[] data = (object[])innerData;
DataHelper helper = (DataHelper)Activator.CreateInstance((Type)data[0]);
if (string.IsNullOrEmpty(helper.ConnectionString))
helper.ConnectionString = data[1].ToString();
OperationArgs args = new OperationArgs(helper);
QueryBuilder query = new QueryBuilder(helper.CreateParameters());
// 1. 子實體集合
if (member.IsDefined(typeof(SubEntityListAttribute), true))
{
var attr = DataMappingManager.GetSubEntityListKey(entityType, propName);
if (attr != null)
{
var list = (IDataEntityList)Activator.CreateInstance(attr.EntityListType);
Type refType = list.ModelType;
object pkValue = ((DataEntity)_target).GetValue(attr.PrimaryKey);
if (pkValue != null)
{
query.Append(QueryCompare.Equal, attr.ForeignKey, pkValue);
if (!string.IsNullOrEmpty(attr.Condition))
query.Append(QueryRelation.And, attr.Condition);
list = SelectOperator.Select(args, refType, query, null, null);
returnedMessage = new ReturnMessage(list, null, 0, null, mcm);
((DataEntity)_target).SetValue(propName, returnedMessage.ReturnValue);
}
}
}
// 2. 參考實體
else if (member.IsDefined(typeof(ReferenceEntityAttribute), true))
{
var attr = DataMappingManager.GetReferenceEntityKey(entityType, propName);
object fkValue = ((DataEntity)_target).GetValue(attr.ForeignKey);
if (fkValue != null)
{
query.Append(QueryCompare.Equal, attr.PrimaryKey, fkValue);
object result = GetOperator.Get(args, attr.ReferenceType, query, null, null);
returnedMessage = new ReturnMessage(result, null, 0, null, mcm);
((DataEntity)_target).SetValue(propName, returnedMessage.ReturnValue);
}
}
// 3. 參考屬性
else if (member.IsDefined(typeof(ReferencePropertyAttribute), true))
{
var attr = DataMappingManager.GetReferencePropertyKey(entityType, propName);
object fkValue = ((DataEntity)_target).GetValue(attr.ForeignKey);
if (fkValue != null)
{
query.Append(QueryCompare.Equal, attr.PrimaryKey, fkValue);
DataEntity refEntity = GetOperator.Get(args, attr.ReferenceType, query, null, null);
if (refEntity != null)
{
object result = refEntity.GetValue(attr.ReferencePropertyName);
returnedMessage = new ReturnMessage(result, null, 0, null, mcm);
((DataEntity)_target).SetValue(attr.Property, result);
}
}
}
helper.Dispose();
}
}
}
// 未處理或非目標訊息,傳遞給下一個 Sink
if (returnedMessage == null)
returnedMessage = (IMethodReturnMessage)_nextSink.SyncProcessMessage(msg);
else if (isAsync)
returnedMessage = null; // 非同步不回傳自訂結果
}
}
IMethodCallMessage + get_/FieldGetter/GetValue + 非填充狀態SubEntityListAttribute、ReferenceEntityAttribute、ReferencePropertyAttributeoldValue == null 才觸發載入(避免重複查詢)InnerCache 取得 DataHelper、連線字串ReturnMessage 包裝結果,SetValue 寫回實體屬性helper.Dispose()| 亮點 | 說明 |
|---|---|
| 屬性驅動 | 領域模型以 Attribute 宣告導航關聯,零侵入業務邏輯 |
| 按需載入 | 首次存取才查詢,避免 N+1 問題前提是合理使用 |
| 快取操作上下文 | 避免重複建立 DataHelper/Connection |
| 多導航類型統一 | 集合/單實體/純屬性三合一 |
| 非同步透傳 | 非同步呼叫不攔截結果,避免阻塞 |
ContextBoundObject Proxy 開銷InnerIsFill 標記區分序列化填充與業務存取DataCacheKeyManager、InnerCache、DataMappingManager 等基礎設施LazyLoadableSink 完整實現了透明延遲載入:攔截導航屬性 get,空值時自動查詢資料庫並快取。三種導航類型(集合、實體、屬性)統一處理,展示 AOP 在資料存取層橫切關注點的強大表達力。現代替代方案包括 EF Core 導航屬性攔截、動態 Proxy (Castle/DynamicProxy)、原始碼產生器。