[C#]Lite ORM Tools : Dapper + Slapper.AutoMapper 一對多:多層MODEL自動對應

  • 1506
  • 0
  • ORM
  • 2020-05-06

Dapper + Slapper.AutoMapper 一對多映射實作及BUG註記

【天啊!Slapper.AutoMapper自己腦補幫我寫完了程式,是在哈囉?】

前言

  1. Slapper非常吃重命名規則,而且會以底線Underline作為資料分支的重要指標,
    所以原資料表欄位命名若有出現底線,則必須全部取代掉
  2. 欄位名稱中必須有「ID」這個字眼,當作分層的主鍵(亦可另外指定主鍵)

 

基本介紹

開發環境

  1. Microsoft Visual Studio .NET 2017 Professional
  2. Dapper v2.0.30
  3. Slapper.AutoMapper v1.0.0.9

參考資料

取得方式

  • Nuget

 

已知須注意的狀況

  1. Oracle最大欄位命名長度為30,在Slapper.AutoMapper層數愈多,名稱愈長是確定的。
    所以在一開始的時候,自己的「命名規則」就顯得特別重要!
  2. 若Model的層數太深,Slapper.AutoMapper的效能會有斷崖式的落差。
    所以多層數的Model,要適時拆開成多組,再組合起來會比較好。
    我自己測試的狀況是… 6-7層要50000ms,拆開來跑的話,怎麼跑也不超過100ms。

 

實作

 

MODEL

    /// <summary>
    /// 事業群
    /// </summary>
    public class MyGroupModel
    {
        /// <summary>
        /// 事業群ID
        /// </summary>
        public int ID { get; set; }

        /// <summary>
        /// 管轄處
        /// </summary>
        public List<DIV> DIV { get; set; }
    }
	
    /// <summary>
    /// 處
    /// </summary>
    public class DIV
    {
        /// <summary>
        /// 處ID
        /// </summary>
        public int ID { get; set; }

        /// <summary>
        /// 處級名稱
        /// </summary>
        public int NAME { get; set; }

        /// <summary>
        /// 管轄部門
        /// </summary>
        public List<DEPT> DEPT { get; set; }
    }
	
    /// <summary>
    /// 部門
    /// </summary>
    public class DEPT
    {
        /// <summary>
        /// 部門ID
        /// </summary>
        public int ID { get; set; }

        /// <summary>
        /// 部門名稱
        /// </summary>
        public int NAME { get; set; }
    }

 

PROGRAM

public List<MyGroupModel> getGroupModel(string GroupID, string DeptID)
{
	DynamicParameters parameter = new DynamicParameters();
	DataProviderBase db = new OracleDataProvider(connectionString);

	parameter = new DynamicParameters();

	string select_cmd = @"
            SELECT MYGROUP.GROUP_ID            ID
                 , MYDIV.DIVISION_ID           DIV_ID
                 , MYDIV.DIVISION_NAME         DIV_NAME
                 , MYDEPT.DEPTARTMENT_ID       DIV_DEPT_ID
                 , MYDEPT.DEPTARTMENT_NAME     DIV_DEPT_NAME
              FROM ORADB.MYGROUP MYGROUP     /*人事-事業群檔*/
         LEFT JOIN ORADB.MYDIVISION MYDIV    /*人事-處檔*/
                ON MYGROUP.GROUP_ID = MYDIV.MYDIV_GROUPID
         LEFT JOIN ORADB.MYDEPARTMENT MYDEPT /*人事-部門檔*/
                ON MYDIV.MYDIV_ID = MYDEPT.MYDEPT_DIVID
             WHERE MYGROUP.GROUP_ID = :GroupID
               AND MYDEPT.DEPTARTMENT_ID = :DeptID
                         ";

	parameter.Add(":GroupID", GroupID);
	parameter.Add(":DeptID", DeptID);
	db = new OracleDataProvider(connectionString);

	//此處為使用 Dapper取回資料
	var p2M = db.GetDataModel<dynamic>(select_cmd, CommandType.Text, parameter);

	//此處透過Slapper.AutoMapper將資料填成model的資料結構
	var ss = (Slapper.AutoMapper.MapDynamic<MyGroupModel>(p2M)).ToList();

	return ss;
}

 

 

後記

(關於這個部份,有網友回覆提醒我原因了,特別感謝Kevin,我將查詢的結果寫在最後了,記得要看哦!!)

Slapper.AutoMapper「好像」有個BUG,

會說好像是因為,也許它也是「Auto」的一個部份???

請看下面兩段程式,第一層的ID一樣,它就自動幫我組合了。

 

我是省下了一個組合的功夫...

但…這真的是我們需要的結果嗎?

        /// <summary>
        /// 查詢IT&HR的組織
        /// </summary>
        /// <returns></returns>
        public List<MyGroupModel> getITnHRModel_1(string GroupID)
        {
            List<MyGroupModel> ITModel = new List<MyGroupModel>();
            List<MyGroupModel> HRModel = new List<MyGroupModel>();

            ITModel = getGroupModel(GroupID, "IT");
			
            HRModel = getGroupModel(GroupID, "HR");
			
            //還沒組合IT&HR Model
            //HRModel就已經被Slapper組合完了???
			
            return HRModel;
        }
		
        /// <summary>
        /// 查詢IT&HR的組織
        /// </summary>
        /// <returns></returns>
        public List<MyGroupModel> getITnHRModel_2(string GroupID)
        {
            List<MyGroupModel> result = new List<MyGroupModel>();

            getGroupModel(GroupID, "IT");
			
            result = getGroupModel(GroupID, "HR");
			
            //result又被Slapper組合起來了???
			
            return result;
        }		

 

2020/05/05更新:

經網友Kevin提醒,原來是我忽略了keepCache參數,而造成的影響。

原廠說明:https://github.com/SlapperAutoMapper/Slapper.AutoMapper/blob/master/Slapper.AutoMapper/Slapper.AutoMapper.cs

        /// <summary>
        /// Converts a dynamic object to a type <typeparamref name="T"/>.
        /// 
        /// Population of complex nested child properties is supported by underscoring "_" into the
        /// nested child properties in the property name.
        /// </summary>
        /// <typeparam name="T">Type to instantiate and automap to</typeparam>
        /// <param name="dynamicObject">Dynamic list of property names and values</param>
        /// <param name="keepCache">If false, clears instance cache after mapping is completed. Defaults to true, meaning instances are kept between calls.</param>
        /// <returns>The type <typeparamref name="T"/></returns>
        /// <exception cref="ArgumentException">Exception that is thrown when the <paramref name="dynamicObject"/> cannot be converted to an IDictionary of type string and object.</exception>
        public static T MapDynamic<T>( object dynamicObject, bool keepCache = true)
        {
            return (T)MapDynamic(typeof(T), dynamicObject, keepCache);
        }

針對keepCache的描述翻譯就是:若是false,會在執行之後清除Cache。預設為true,表示會在保留前次結果。

所以呢,不是一個bug,而是Slapper.Automapper,真的很自作主張方便。真的可以節省一 些時間。 給個讚!!

 Written By Felix Hsieh