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

AOP 實戰:延遲載入 LazyLoad 實作原始碼深度解析(下)

     編輯:關於C語言

核心組件:LazyLoadableSink 訊息攔截器

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; // 非同步不回傳自訂結果
    }
}

關鍵邏輯流程圖解

  1. 攔截判斷:同步訊息 + IMethodCallMessage + get_/FieldGetter/GetValue + 非填充狀態
  2. 導航屬性識別:反射檢查 SubEntityListAttribute、ReferenceEntityAttribute、ReferencePropertyAttribute
  3. 空值檢查:oldValue == null 才觸發載入(避免重複查詢)
  4. 快取操作上下文:InnerCache 取得 DataHelper、連線字串
  5. 三類導航分支:
    • 子實體集合:依主鍵查詢關聯集合
    • 參考實體:依外鍵查詢單一實體
    • 參考屬性:查詢實體後取特定屬性值
  6. 結果注入:ReturnMessage 包裝結果,SetValue 寫回實體屬性
  7. 資源釋放:helper.Dispose()

設計亮點與權衡

亮點說明
屬性驅動領域模型以 Attribute 宣告導航關聯,零侵入業務邏輯
按需載入首次存取才查詢,避免 N+1 問題前提是合理使用
快取操作上下文避免重複建立 DataHelper/Connection
多導航類型統一集合/單實體/純屬性三合一
非同步透傳非同步呼叫不攔截結果,避免阻塞

已知限制

  • ContextBoundObject Proxy 開銷
  • 僅攔截同步呼叫,非同步屬性存取不觸發
  • 依賴 InnerIsFill 標記區分序列化填充與業務存取
  • 硬編碼 DataCacheKeyManager、InnerCache、DataMappingManager 等基礎設施

總結

LazyLoadableSink 完整實現了透明延遲載入:攔截導航屬性 get,空值時自動查詢資料庫並快取。三種導航類型(集合、實體、屬性)統一處理,展示 AOP 在資料存取層橫切關注點的強大表達力。現代替代方案包括 EF Core 導航屬性攔截、動態 Proxy (Castle/DynamicProxy)、原始碼產生器。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved