需求情境:
現有資料表 T_USER,想要以 Kuick 存取其資料,但是不想花時間寫 UserEntity,這該如何達成?
答:
Entity 支援 dynamic,就直接如下圖呈現的方式使用 Entity 即可。
<與 dynamic 相關的繼承樹>
先看看 Entity 的繼承樹:
依據 .Net 4 的定義,凡實作 IDynamicMetaObjectProvider 介面的類別,執行期方可為 dynamic 物件操作。相關的資料請參閱 MSDN:
<啟用 dynamic 功能>
如同其他功能啟用的定義規則,分成全域與 Entity 層級的啟用定義,系統預設開啟全域的 dynamic 功能,Entity 預設也開啟 dynamic 功能,而 Entity<T> 預設則關閉,如需調整可以經由下面方法進行:
1. 停用全域 dynamic 功能
在 Web.config / App.config 裡,如下設定:
<configuration>
<Kuick>
<application>
<add group="Data" name="EnableDynamic" value="False"/>
</application>
</Kuick>
</configuration>
2. 停用 Entity 層級 dynamic 功能
以 UserEntity 為例:
// Kuick.Data.DynamicData -- 使用 virtual 修飾子
public virtual bool EnableDynamic
{
get
{
return true;
}
}
// Kuick.Data.Entity<T> -- 使用 override 修飾子
public override bool EnableDynamic
{
get
{
return false;
}
}
// UserEntity -- 使用 new 修飾子
public new bool EnableDynamic
{
get
{
return false;
}
}
啟用 dynamic 會損耗執行效能,請依據專案需求與下面說明進行設定:
.如果 UserEntity 停用 Entity 層級的 dynamic 功能,即使啟用全域 dynamic 功能,UserEntity 還是沒有 dynamic 功能。
.如果 UserEntity 啟用 Entity 層級的 dynamic 功能,但是停用全域 dynamic 功能,UserEntity 還是沒有 dynamic 功能。
.如果要關閉全部的 dynamic 功能,請從 config 檔設定。
.如果要單獨停用 UserEntity 的 dynamic 功能,請從 Entity 層級設定。
全域與 Entity 皆預設開啟 dynamic 功能,但是 Entity<T> 預設則是關閉,這代表:
.使用 Entity 查資料,預設可以使用 dynamic 功能。
.使用 UserEntity 之類的實作物件,預設無法使用 dynamic 功能。
<啟用 dynamic 功能而增加的資料暫存>
在 Kuick.Data.IDynamicData 介面裡定義 2 個 property 作為暫存資料儲存處:
1. DataRow Row { get; set; }
Entity 執行 ExecuteQuery, ExecuteStoredProcedure 方法,在最初取得的 DataSet 資料轉成 Entity 集合時,會將每一個 DataRow 存放在這個 property 裡,作為 dynamic 取值時的資料來源。詳細實作請參考:
Kuick.Entity.ExecuteQuery 方法
Kuick.Entity.ExecuteStoredProcedure 方法
Kuick.Data.Extender.ToEntity 方法
2. Anys Record { get; set; }
SqlDatabase 執行 Query 相關查詢時,在最初取得的 DataReader 資料轉成 Entity 集合時,會將當時 DataReader 所在的所有欄位值轉成 Anys 物件存放在這個 property 裡,作為 dynamic 取值時的資料來源。詳細實作請參考:
Kuick.Data.SqlReader.Bind 方法
<如何實作 dynamic 功能>
因為 DynamicEntity 實作 TryGetMember 方法,所以 Entity 支援 property 取值部份的 dynamic 功能。詳細實作請參考:
Kuick.Data.DynamicData.TryGetMember 方法
由於資料暫存於 DynamicData 的 Row 或是 Record 裡,實作 TryGetMember 方法需同時判斷這 2 個 property。這項功能的實作分成兩部份說明:
1. 如何指定取值欄位:
為了加快開發時期的效率,欄位的指定會消除英文字母大小寫與下標線的困擾,所以,如果資料庫欄位名稱為 USER_NAME,程式開發可以指定成 userName, UserName, username 甚至是 U_sernA_me 也可以,只要在去除下標線且轉成小寫字母後比對結果相同時,都可正確取得資料。
2. 回傳值型態為何:
IDynamicData 裡定義的 AsAny property 預設為 false,所以 TryGetMember 回傳值型態預設為 object,當將 AsAny 設定為 true 時,TryGetMember 回傳值型態將轉為 Any 物件。
IDynamicData 裡定義 AsDynamic 方法,執行這個方法會將 AsAny 改成 true,同時將本身轉為 dynamic 物件回傳,所以經由這個方法取得的物件,TryGetMember 回傳值型態為 Any 物件。
為何需要實作不同型態的回傳值?
回傳值為 object 是因為處理序列化成 JSON 格式時的需求。
回傳值為 Any 是因為協助開發者處理不同資料型態轉換的需求。
<如何使用 dynamic 功能>
回到一開始例子的原始碼:
// 使用 Entity 執行選取指令
List<Entity> users = Entity.ExecuteQuery("Select * From T_USER");
foreach(Entity user in users) {
// Entity 物件轉成 dynamic
dynamic d = user.AsDynamic();
// 依命名慣例取得欄位資料
// 再依自行認知的資料格式轉成適合的型態
string userName = d.UserName.ToString();
DateTime birthday = d.Birthday.ToDateTime();
int level = d.Level.ToInteger();
bool actived = d.Actived.ToBoolean();
string email = d.Email.ToString();
Gender gender = d.Gender.ToEnum<Gender>();
}
Entity 類別可以直接執行查詢指令 (任何查詢指令皆可),資料取得後透過 AsDynamic 方法轉成 dynamic 物件,自此之後,對於查詢結果裡任何欄位值,皆可指定取出,由於回傳的型態是 Any,所以依據自己對於資料格式的認知,使用 Any 內建的轉型方法滿足專案開發時的商業邏輯。
這裡將 Any 提供的轉型方法條列如下,從方法名稱就可清楚看出轉出的型態為何,如需了解實作細節,再請參考 Kuick.Any 類別原始檔:
01. ToString
02. ToBoolean
03. ToDateTime
04. ToByte
05. ToChar
06. ToInteger
07. ToFloat
08. ToDouble
09. ToShort
10. ToLong
11. ToDecimal
12. ToColor
13. ToByteArray
14. ToEnum
========================================
本文講述的功能方才於近日剛完成,這部份的原始檔將包含在下一次更新裡,時間會在 2012-11-10 前。
========================================
鐵人賽分享列表:Kuick Application & ORM Framework
開放原始碼專案:kuick.codeplex.com
直接下載原始碼:Kuick
下載相關文件檔:C# Code Conventions and Design Guideline
相關教學影片區:Kuick on YouTube