一行代碼調用實現帶字段選取+條件判斷+排序+分頁功能的加強ORM框架

問題:3行代碼

    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

曙光:V5版本


    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>();

 

  目的達到了,原來只要肯想法,辦法仍是很簡單的,心中一陣竊喜:)

精簡:讓用戶再懶一點


  過了一下子,再反覆看看上面這一行代碼,發現了幾個問題:

  1. Select 方法無法指定要選擇的表字段;
  2. Where,OrderBy,ToList 都須要指定泛型的具體類型,既然From<Users> 最開始已經指定過了,那麼後面的方法再指定<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詳細代碼--

 成功:一行代碼的真相

 

爲了讓你們更清楚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 各個類的使用場景:

  • GOQL :解決單實體類的R(Read);
  • OQL+EntityQuery<T>: 解決單實體類的CRUD;
  • OQL+EntityContainer: 解決多實體類的R

-----分界線----------------

感謝廣大PDF.NET的會員和用戶朋友一直以來的支持,你的批評是咱們進步的力量!歡迎加入框架的開源項目

相關文章
相關標籤/搜索