通常,我們都是在業務層和界面層使用枚舉類型,這能夠為我們編程帶來便利,但在數據訪問層,不使用枚舉類型,因為很多數據庫都不支持,比如我們現在用的SqlServer2008就不支持枚舉類型的列,用的時候也是將枚舉類型轉換成int 類型,數據庫存儲的是int 類型的數據,在訪問數據的時候進行枚舉類型和int類型的轉換,例如下面的例子:
public enum RoleNames { User, Manager, Admin }
假設有一個實體類Users,如果實體類不支持枚舉類型,得這樣使用(下面的示例都以PDF.NET的ORM框架使用來說明):
//獲取一個實體類: Users user=new Users(); user.ID=1; if(EntityQuery<Users>.Fill(user)) { RoleNames rn=(RoleNames)user.RoleID; Console.Write("Role Name:"+rn); } //更新實體類: Users user=new Users(); user.ID=1; user.RoleID=(int)RoleNames.Admin; EntityQuery<Users>.Instance.Update(user);
查詢和更新操作都得對枚舉類型進行轉換,不方便,雖然如此,我們大部分情況下還是將就了,在訪問數據庫的時候這么轉換下。這種情況下EF 5.0 之前也不例外,都是社區用戶的強烈要求,在EF5.0版本之后才加入支持實體類枚舉屬性的。
既然使用枚舉還要將實體類的屬性轉換下,為何不直接將實體類的屬性定義成枚舉類型?
修改下Users類型的定義:
public partial class Users : EntityBase { //其它部分定義略 public RoleNames RoleID { get { return getProperty<RoleNames>("RoleID"); } set { setProperty("RoleID", value); } } }
直接使用這個修改過的實體類來插入、修改數據,是沒有問題的:
//更新實體類: Users user=new Users(); user.ID=1; user.RoleID=RoleNames.Admin; EntityQuery<Users>.Instance.Update(user);
但是查詢實體類的時候會有點小問題,雖然能夠正確的從數據庫查詢,但查看枚舉屬性的時候會報類型轉換錯誤:
//獲取一個實體類: Users user=new Users(); user.ID=1; if(EntityQuery<Users>.Fill(user)) { RoleNames rn=user.RoleID; Console.Write("Role Name:"+rn); }
跟蹤代碼發現,user.RoleID 對應的SqlReader 的結果類型是int ,因為數據庫的RoleID 列沒法定義成枚舉類型,如果要將實體類的屬性定義成枚舉類型,那么在SqlReader讀取的時候,必須進行類型轉換:
user.RoleID=(RoleNames)reader["RoleID"];
幸好PDF.NET的實體類認為“實體類是數據的容器”,內部采用一個object[] 保存來自數據庫的原始數據,而在使用數據的時候,才來進行類型轉換,因此框架原來查詢數據、插入、更新數據的地方,都不用做任何修改,只需要修改下 getProperty<T>("fieldName") 涉及的部分:
public static T ChangeType<T>(object Value) { if (Value is T) return (T)Value; else if (Value == DBNull.Value || Value == null) { if (typeof(T) == typeof(DateTime)) { //如果取日期類型的默認值 0001/01/01 ,在WCF JSON序列化的時候,會失敗。 object o = new DateTime(1900, 1, 1); return (T)o; } else return default(T); } else { //edit at 2011.5.16 //如果 Value為 decimal類型,T 為double 類型, (T)Value 將發生錯誤 //edit at 2013.8.9 支持枚舉類型 if (typeof(T).IsEnum) return (T)Value; else return (T)Convert.ChangeType(Value, typeof(T)); } }
使用PDF.NET框架的V4.X 版本(包括V4.6之前的版本)用戶,只需要打開 CommonUtil.cs 文件,找到該方法,將
return (T)Convert.ChangeType(Value, typeof(T));
修改為:
if (typeof(T).IsEnum) return (T)Value; else return (T)Convert.ChangeType(Value, typeof(T));
即可。
經過測試,通過這樣的修改,框架就可以支持實體類使用枚舉類型了。
為什么修改如此簡單?前面已經說過,PDF.NET的實體類是數據的容器,也就是說,我們在內存中將某個屬性的值直接設置為枚舉類型的值,也可以將內存中的Int 類型的來自數據庫的值,在運行時轉換成枚舉類型。這樣,使得PDF.NET的實體類的屬性類型可以不必跟數據庫的字段類型嚴格對應,只要類型相容即可。這個特點為系統移植數據庫平台提供了很大的便利,比如Oracle 沒有Decimal類型,沒有real 類型,要使用非整形的數字類型,只有使用Number類型,那么為SqlServer設計使用的實體類,一般情況下也可以直接在Oracle下使用。
下面的代碼是一個完整的使用實體類的枚舉屬性的例子:
using System; using System.Collections.Generic; //using System.Linq; using System.Text; using PWMIS.DataMap.Entity; using PWMIS.Common; namespace OQLTest { public enum RoleNames { User, Manager, Admin } public partial class Users : EntityBase { public Users() { TableName = "LT_Users"; EntityMap = EntityMapType.Table; //IdentityName = "標識字段名"; IdentityName = "ID"; //PrimaryKeys.Add("主鍵字段名"); PrimaryKeys.Add("ID"); } protected override void SetFieldNames() { PropertyNames = new string[] { "ID", "UserName", "Password", "NickName", "RoleID", "Authority", "IsEnable", "LastLoginTime", "LastLoginIP", "Remarks", "AddTime" }; } /// <summary> /// /// </summary> public System.Int32 ID { get { return getProperty<System.Int32>("ID"); } set { setProperty("ID", value); } } /// <summary> /// /// </summary> public System.String UserName { get { return getProperty<System.String>("UserName"); } set { setProperty("UserName", value, 50); } } /// <summary> /// /// </summary> public System.String Password { get { return getProperty<System.String>("Password"); } set { setProperty("Password", value, 50); } } /// <summary> /// /// </summary> public System.String NickName { get { return getProperty<System.String>("NickName"); } set { setProperty("NickName", value, 50); } } /// <summary> /// /// </summary> public RoleNames RoleID { get { return getProperty<RoleNames>("RoleID"); } set { setProperty("RoleID", value); } } //用下面的方式處理實體類的子實體類問題 UserRoles _roles; public UserRoles Roles { get { if (_roles == null) { _roles = new UserRoles() { ID = this.RoleID }; EntityQuery<UserRoles>.Fill(_roles); } return _roles; } } /// <summary> /// /// </summary> public System.String Authority { get { return getProperty<System.String>("Authority"); } set { setProperty("Authority", value, 250); } } /// <summary> /// /// </summary> public System.Boolean IsEnable { get { return getProperty<System.Boolean>("IsEnable"); } set { setProperty("IsEnable", value); } } /// <summary> /// /// </summary> public System.DateTime LastLoginTime { get { return getProperty<System.DateTime>("LastLoginTime"); } set { setProperty("LastLoginTime", value); } } /// <summary> /// /// </summary> public System.String LastLoginIP { get { return getProperty<System.String>("LastLoginIP"); } set { setProperty("LastLoginIP", value, 20); } } /// <summary> /// /// </summary> public System.String Remarks { get { return getProperty<System.String>("Remarks"); } set { setProperty("Remarks", value, 150); } } /// <summary> /// /// </summary> public System.DateTime AddTime { get { return getProperty<System.DateTime>("AddTime"); } set { setProperty("AddTime", value); } } } }
測試程序:
Users user = new Users() { NickName = "pdf.net", RoleID= RoleNames.Admin }; OQL q0 = OQL.From(user) .Select() .Where(user.NickName, user.RoleID) .OrderBy(user.ID) .END; q0.SelectStar = true; Console.WriteLine("q0:one table and select all fields \r\n{0}", q0); Console.WriteLine(q0.PrintParameterInfo()); var userList= EntityQuery<Users>.QueryList(q0); if (userList.Count > 0) { Users u = userList[0]; Console.WriteLine("User Type is:"+u.RoleID.ToString()); u.RoleID = RoleNames.User; EntityQuery<Users>.Instance.Update(u); }
程序輸出:

數據庫結果界面:

--------------分界線----------------------
PDF.NET 開發框架是國產的開發框架,支持SQL-MAP、ORM和數據控件 三種編程模型,可以一種或者三種混合使用,是開源的開發框架,供廣大.net開發朋友在EF,NH之外,提供第三中選擇。歡迎加入PDF.NET開源技術團隊。
相關鏈接:
