iT邦幫忙

第 11 屆 iT 邦幫忙鐵人賽

DAY 14
5

Multiple Mapping 底層原理

這邊先以一個簡單Demo帶讀者了解Dapper Multi Mapping 概念

  1. 按照泛型類別參數數量建立對應數量的Mapping Func集合
  2. Mapping Func建立邏輯跟Query Emit IL一樣
  3. 呼叫使用者的Custom Mapping Func,其中參數由前面動態生成的Mapping Func而來
public static class MutipleMappingDemo
{
  public static IEnumerable<TReturn> Query<T1, T2, TReturn>(this IDbConnection connection, string sql, Func<T1, T2, TReturn> map)
    where T1 : Order, new() 
    where T2 : User, new() //這兩段where單純為了Demo方便
  {
    //1. 按照泛型類別參數數量建立對應數量的Mapping Func集合
    var deserializers = new List<Func<IDataReader, object>>();
    {
      //2. Mapping Func建立邏輯跟Query Emit IL一樣
      deserializers.Add((reader) =>
      {
        var newObj = new T1();
        var value = default(object);
        value = reader[0];
        newObj.ID = value is DBNull ? 0 : (int)value;
        value = reader[1];
        newObj.OrderNo = value is DBNull ? null : (string)value;
        return newObj;
      });

      deserializers.Add((reader) =>
      {
        var newObj = new T2();
        var value = default(object);
        value = reader[2];
        newObj.ID = value is DBNull ? 0 : (int)value;
        value = reader[4];
        newObj.Name = value is DBNull ? null : (string)value;
        return newObj;
      });
    }


    using (var command = connection.CreateCommand())
    {
      command.CommandText = sql;
      using (var reader = command.ExecuteReader())
      {
        while (reader.Read())
        {
          //3. 呼叫使用者的Custom Mapping Func,其中參數由前面動態生成的Mapping Func而來
          yield return map(deserializers[0](reader) as T1, deserializers[1](reader) as T2);
        }
      }
    }
  }
}

以上概念就是此方法的主要邏輯,接著講其他細節部分

支持多組類別 + 強型別返回值

Dapper為了強型別多類別Mapping使用多組泛型參數方法方式,這方式有個小缺點就是沒辦法動態調整,需要以寫死方式來處理。

舉例,可以看到圖片GenerateMapper方法,依照泛型參數數量,寫死強轉型邏輯,這也是為何Multiple Query有最大組數限制,只能支持最多6組的原因。
20191001173320.png

多類別泛型緩存算法

  • 這邊Dapper使用泛型類別強型別保存多類別的資料
    20191001175139.png
  • 並配合繼承共用Identity大部分身分驗證邏輯
  • 提供可override的GetType方法,來客製泛型比較邏輯,避免造成跟Non Multi Query緩存衝突

20191001175600.png
20191001175707.png

Dapper Query Multi Mapping的Select順序很重要

因為SplitOn分組基礎依賴於Select的順序,所以順序一錯就有可能屬性值錯亂情況。

舉例 : 假如上面例子的SQL改成以下,會發生User的ID變成Order的ID;Order的ID會變成User的ID。

select T2.[ID],T1.[OrderNo],T1.[UserID],T1.[ID],T2.[Name] from [order] T1
left join [User] T2 on T1.UserId = T2.ID  

原因可以追究到Dapper的切割算法

  1. 首先倒序方式處理欄位分組(GetNextSplit方法可以看到從DataReader Index大到小查詢)
    20191002022109.png
  2. 接著倒序方式處理類別的Mapping Emit IL Func
  3. 最後反轉為正序,方便後面Call Func對應泛型使用
    20191002021750.png
    20191002022208.png
    20191002022214.png

上一篇
【深入Dapper.NET源碼】Query Multi Mapping 使用方式
下一篇
【深入Dapper.NET源碼】QueryMultiple(多個結果)底層原理
系列文
🌊 進階學習 ADO.NET、Dapper、Entity Framework 30

尚未有邦友留言

立即登入留言