PDF.NET 是一個開源的數據開發框架,它的特色是簡單、輕量、快速,易上手,並且是一個註釋完善的國產開發框架,受到很多朋友的歡迎,也在咱們公司的項目中屢次使用。可是,PDF.NET比起EF來,仍然有很大的劣勢,主要就是用起來沒有EF簡單,這個問題飽受廣大朋友的批評,但我很感謝這些朋友,他們的批評纔是框架進步的動力,爲此,以前我發表了《來一點反射和Emit,讓ORM的使用極度簡化》 這篇文章,使得再也不須要定義實體類,只須要有接口便可訪問數據庫:html
原文的代碼:sql
static void TestDynamicEntity() { ITable_User user = EntityBuilder.CreateEntity<ITable_User>(); //若是接口的名稱不是"ITableName" 這樣的格式,那麼須要調用 MapNewTableName方法指定 //((EntityBase)user).MapNewTableName("Table_User"); OQL qUser = OQL.From((EntityBase)user).Select(user.UID, user.Name, user.Sex).END; List<ITable_User> users = EntityQuery.QueryList<ITable_User>(qUser, MyDB.Instance); }
這段程序花了3行代碼來作一個查詢,仍是有點繁瑣。若是不是這種接口類型的動態實體類,能夠經過下面的擴展方法來簡化查詢:數據庫
public static List<T> ToList<T>(this OQL q) where T:EntityBase,new() { return EntityQuery<T>.QueryList(q); } public static OQL From<T>() where T : EntityBase, new() { T entity = new T(); return OQL.From(entity); }
有了這2個「擴展」方法,咱們的查詢能夠一行完成了:框架
List<User> users=OQL.From<User>.ToList<User>();
等同於ide
List<User> users=OQL.From<User>.Select().END.ToList<User>();
但這樣的寫法無法選擇須要的列,若是要附加查詢條件,在V5.0以前,還得這樣作:測試
User user=new User(){UserName="zhangsan",Password="abc."} List<User> users=OQL.From(user) .Select(user.ID,user.UserName,user.Password) .Where(user.UserName,user.Password) .END .ToList<User>();
這樣查詢還得須要2行代碼,並且沒有利用上泛型的優點,最後的ToList還得指定類型User ,這樣寫仍然不優雅。ui
PDF.NET Ver 5.0 在通過了脫胎換骨般的重構後,OQL增長了大量特性,OQL方法支持Lambda表達式語法,支持泛型,咱們前面的代碼有望獲得簡化:this
Users user = new Users(); var userList = OQL.From(user) .Select(user.UserName, user.ID) .Where<Users>((cmp,u)=>cmp.Compare(u.ID,">",100) //.OrderBy(p => p.Desc(user.UserName).Asc(user.ID)) //2種排序方式 .OrderBy<Users>((o,u) => { o.Desc(u.UserName); }) .END .ToList<Users>();
OQL V5.0.0的寫法還得藉助Users 的對象實例來選取字段,或者動態排序,仍然多了一行代碼:spa
Users user = new Users();
這一行代碼儘管可以給我在Where條件相等比較上代來便利,直接將條件值傳入進去,但無論怎麼說,一個查詢仍是讓我多寫了一行代碼,沒有作到EF那樣,一行代碼解決問題。這多出來的一行代碼,讓PDF.NET的用戶朋友很不滿意的,主要就是,EF均可以一行查詢出來,PDF.NET爲何不行?太麻煩了!設計
我經常在想,爲何「客戶」這麼難以伺候,就多寫了一行實體類的實例化的代碼,這都顯得麻煩麼?還有各類好處呢,PDF.NET基於實體類的實例調用特性,構築起了OQL支持複雜查詢的特性(參見 《ORM查詢語言(OQL)簡介--高級篇(續):廬山真貌》 ),SQL可以支持的,OQL基本上都可以支持了。
可是,我說的好處彷佛很難讓個人「客戶」朋友門滿意,仍是那句話:
EF均可以作到,PDF.NET爲何作不到?
個人理想是,EF能夠作到的,PDF.NET 也儘可能作到,EF作不到的,PDF.NET 要作到!
不然,在衆多ORM框架的圍攻下,PDF.NET很難生存下去。EF都開源了,說明作ORM競爭太激烈了,沒有特點,更本無法生存。
在考慮了幾天以後,我認爲基於如今PDF.NET V5.0的新版核心,有可能真正實現一行代碼進行數據查詢的。
問題所在也很清楚了,就是那個實體類的申明語句讓我很尷尬:
Users user = new Users();
只要幹掉它,我就成功了!
而這,徹底能夠在下面的方法中作「手腳」實現:
public static OQL From<T>() where T : EntityBase,new() { T entity=new T(); return new OQL(entity); }
很簡單嘛,這樣就能夠一行代碼實現查詢了:
var userList = OQL.From<Users>() .Select() .Where<Users>((cmp,u)=>cmp.Compare(u.ID,">",100) .OrderBy<Users>((o,u) => { o.Desc(u.UserName); }) .END .ToList<Users>();
目的達到了,原來只要肯想法,辦法仍是很簡單的,心中一陣竊喜:)
過了一下子,再反覆看看上面這一行代碼,發現了幾個問題:
爲了讓框架的「客戶」再少敲幾個字符,我決定構造一個OQL的泛型類,這樣它相關的操做方法就不須要反複製定具體類型了,同時想法解決問題1。因而,這個新類以下定義:
public class GOQL<T> where T:class { protected internal OQL currentOQL; private T currentEntity; public delegate object[] SelectFieldFunc(T s); public GOQL1<T> Select(SelectFieldFunc func) { return new GOQL1<T>(this, currentOQL.Select(func(currentEntity))); } /* 其它方法略 */ }
有了SelectFieldFunc 這個委託,就能夠給Select 方法使用了,選擇指定的字段數據:
currentOQL.Select(func(currentEntity))
接下來,按照OQL的設計思路,進行SQL 語句分層 設計,目前只打算支持Where 和OrderBy字句,因此須要定義下面的子類:
public class GOQL1<T> : GOQL2<T> where T : class { public GOQL2<T> Where(OQLCompareFunc<T> func) {} } public class GOQL2<T> where T : class { public GOQL<T> OrderBy(OQLOrderAction<T> orderAct) {} }
因爲SQL語句不必定須要Where子句,能夠直接在 Select 子句後跟Order By 子句,因此讓GOQL1<T>繼承 GOQL2<T> 。
OK,通過這樣的設計,整個GOQL代碼只有95行代碼,沒錯,只有95行,目前尚未寫註釋,詳細代碼請展開看下面的內容:
1 using System; 2 using System.Collections.Generic; 3 using PWMIS.DataProvider.Data; 4 using PWMIS.DataProvider.Adapter; 5 6 namespace PWMIS.DataMap.Entity 7 { 8 public class GOQL<T> where T:class 9 { 10 protected internal OQL currentOQL; 11 private T currentEntity; 12 public delegate object[] SelectFieldFunc(T s); 13 14 public GOQL(OQL oql,T entity) 15 { 16 this.currentOQL = oql; 17 this.currentEntity = entity; 18 } 19 public GOQL1<T> Select() 20 { 21 return new GOQL1<T>(this, currentOQL.Select()); 22 } 23 public GOQL1<T> Select(SelectFieldFunc func) 24 { 25 return new GOQL1<T>(this, currentOQL.Select(func(currentEntity))); 26 } 27 public GOQL<T> Limit(int pageSize, int pageNumber) 28 { 29 this.currentOQL.Limit(pageSize, pageNumber); 30 return this; 31 } 32 public GOQL<T> Print(out string sqlInfo) 33 { 34 sqlInfo = string.Format("SQL:{0}\r\n{1}",currentOQL.ToString(), currentOQL.PrintParameterInfo()); 35 return this; 36 } 37 public List<T> ToList(AdoHelper db ) 38 { 39 return EntityQuery.QueryList<T>(this.currentOQL, db); 40 } 41 public List<T> ToList() 42 { 43 return ToList(MyDB.Instance); 44 } 45 public T ToObject(AdoHelper db) 46 { 47 return EntityQuery.QueryObject<T>(this.currentOQL, db); 48 } 49 public T ToObject() 50 { 51 return ToObject(MyDB.Instance); 52 } 53 public override string ToString() 54 { 55 return currentOQL.ToString(); 56 } 57 } 58 59 public class GOQL1<T> : GOQL2<T> where T : class 60 { 61 private GOQL<T> currentGOQL; 62 private OQL1 currentOQL1; 63 64 public GOQL1(GOQL<T> gq,OQL1 q1):base(gq) 65 { 66 this.currentGOQL = gq; 67 this.currentOQL1 = q1; 68 } 69 70 public GOQL2<T> Where(OQLCompareFunc<T> func) 71 { 72 this.currentOQL1.Where(func); 73 return new GOQL2<T>(currentGOQL); 74 } 75 } 76 77 public class GOQL2<T> where T : class 78 { 79 private GOQL<T> currentGOQL; 80 81 public GOQL2(GOQL<T> gq) 82 { 83 this.currentGOQL = gq; 84 } 85 public GOQL<T> OrderBy(OQLOrderAction<T> orderAct) 86 { 87 OQL4 currentOQL4 = new OQL4(this.currentGOQL.currentOQL).OrderBy<T>(orderAct); 88 return this.currentGOQL; 89 } 90 public GOQL<T> END 91 { 92 get { return this.currentGOQL; } 93 } 94 } 95 }
爲了讓你們更清楚GOQL的結構和它與PDF.NET框架其它部分的關係,請看下面的類圖:
-類圖-
最後,咱們就能夠寫一個真正的測試代碼了:
95行源碼,一行代碼調用實現帶字段選取+條件判斷+排序+分頁功能的加強ORM框架
static void TestGOQL() { string sqlInfo=""; //下面使用 ITable_User 或者 Table_User都可 List<ITable_User> userList = OQL.FromObject<ITable_User>() //.Select() .Select(s => new object[] { s.UID, s.Name, s.Sex }) //僅選取3個字段 .Where((cmp, user) => cmp.Property(user.UID) < 100) .OrderBy((o,user)=>o.Asc(user.UID)) .Limit(5, 1) //限制5條記錄每頁,取第一頁 .Print(out sqlInfo) .ToList(); Console.WriteLine(sqlInfo); Console.WriteLine("User List item count:{0}",userList.Count); }
此次新增了 OQL.FromObject<T>() 方法,類型T便可以是一個普通接口,也能夠是一個PDF.NET的實體類。
有圖有真相,下面是這個測試程序的輸出截圖:
-截圖-
收工,PDF.NET 順利實現一行代碼查詢數據的功能,除了Where 條件的複雜寫法不那麼優美,整體上GOQL,OQL能夠媲美EF了!
注意:GOQL功能,在PDF.NET框架的Ver 5.0.1 版本支持,以前的https://pwmis.codeplex.com/releases/view/104043 PDF.NET_V5.0Beta_20130807 不支持,要獲取框架的最新源碼,請加入本框架的官方QQ羣,詳細聯繫信息請看框架官網 http://www.pwmis.com/sqlmap
最後總結下PDF.NET ORM 各個類的使用場景:
-----分界線----------------
感謝廣大PDF.NET的會員和用戶朋友一直以來的支持,你的批評是咱們進步的力量!歡迎加入框架的開源項目。