Dapper splitOn 的坑

結論

Dapper splitOn 無論是用 "column1" 或 "column1,column2,..." 等方式,一定要符合下列條件,否則怎麼走怎麼採坑。

  • column1, column2, ... 一定要出現在 select 出來的結果欄位名稱中。沒做到就會出現執行階段錯誤:
    • System.ArgumentException: When using the multi-mapping APIs ensure you set the splitOn param if you have keys other than Id 參數名稱: splitOn
  • column1, column2, ... 有對應的 Object Fields 或 Properties 中,其值為 NULL,產生 null。若非 NULL,會產生實體。
  • column1, column2, ... 若沒在對應的 Object Fields 或 Properties 中,就算其值為 NULL。還是會產生全部 Field 或 Property 都是預設值的實體,而不是產生 null 。

緣由

一個專案,搞瘋一個程序猿。

由於此專案其使用的資料庫,官方提供的函式庫在 Entity Framework (Core) 有很多問題及限制。故只能使用 ADO.NET 技術,而且還有限制(...)。

問題1


一直有這類需求:
select
t1.*,
t2.*,
t3.*
from
table1 t1
left join
table2 t2
on
t1.table1_id = t2.table2_id
left join
table3 t3
on
t1.table1_id = t3.table3_id
...

嘗試1

於是先打了以下程式碼。

cn.Query<Table1,Table2,Table3,Table1>(
@"select

t1.*,
t2.*,
t3.*
from
table1 t1
left join
table2 t2
on
t1.table1_id = t2.table2_id
left join
table3 t3
on
t1.table1_id = t3.table3_id
",
(table1,table2,table3) =>
{
    table1.Table2 = table2;
    table1.Table3 = table3;
    return table1;
},
splitOn: "table2_id,table3_id"

);

但在取得結果上一直不正確,Table2 有些與 Table1 及 Table3 相同名稱的欄位會取得預設值或 Table1 的值,但 Table3 卻取得 Table2 的欄位值。

解決1

原來是欄位順序的問題,table2_id 及 table3_id 不是第一個欄位。
WTF...


嘗試2

於是改了以下程式碼。

cn.Query<Table1,Table2,Table3,Table1>(
@"select

t1.*,
t2.table2_id,
t2.*,
t3.table3_id,
t3.*
from
table1 t1
left join
table2 t2
on
t1.table1_id = t2.table2_id
left join
table3 t3
on
t1.table1_id = t3.table3_id
",
(table1,table2,table3) =>
{
    table1.Table2 = table2;
    table1.Table3 = table3;
    return table1;
},
splitOn: "table2_id,table3_id"

);

但會因為 table2_id 及 table3_id 出現了兩次,視 table2 及 table3 欄位重複程度或順序還是會出問題。

解決2

只能再想想既可無視兩者欄位名稱重複,又可切得漂亮的方法。

嘗試3

在每個 Entity (Table1, Table2, Table3) 增加一個文字欄位 SplitOn,怕麻煩可新增 Entity Base Class 其它 Entity 繼承他即可。
記得若是有使用 Entity Framework (Core) Mapping 需要排除 (Ignore) 此欄位。
下查詢。

cn.Query<Table1,Table2,Table3,Table1>(
@"select

t1.*,
t2.table2_id split_on,
t2.*,
t3.table3_id split_on,
t3.*
from
table1 t1
left join
table2 t2
on
t1.table1_id = t2.table2_id
left join
table3 t3
on
t1.table1_id = t3.table3_id
",
(table1,table2,table3) =>
{
    table1.Table2 = table2;
    table1.Table3 = table3;
    return table1;
},
splitOn: "split_on"

);

解決3

目前最漂亮的方法,但有點麻煩需要處理 Entity 的欄位。
最主要是卡在 SplitOn 的欄位必須在 Entity 中出現,不然不會這麼麻煩。

留言