zoukankan      html  css  js  c++  java
  • 一行代码调用实现带字段选取+条件判断+排序+分页功能的增强ORM框架

    问题:3行代码

        PDF.NET 是一个开源的数据开发框架,它的特点是简单、轻量、快速,易上手,而且是一个注释完善的国产开发框架,受到不少朋友的欢迎,也在我们公司的项目中多次使用。但是,PDF.NET比起EF来,仍然有很大的劣势,主要就是用起来没有EF简单,这个问题饱受广大朋友的批评,但我很感谢这些朋友,他们的批评才是框架进步的动力,为此,之前我发表了《来一点反射和Emit,让ORM的使用极度简化》  这篇文章,使得不再需要定义实体类,只需要有接口即可访问数据库

        原文的代码:

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

        等同于

     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 ,这样写仍然不优雅。

    曙光:V5版本


        PDF.NET Ver 5.0 在经过了脱胎换骨般的重构后,OQL增加了大量特性,OQL方法支持Lambda表达式语法,支持泛型,我们前面的代码有望得到简化:

    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 的对象实例来选取字段,或者动态排序,仍然多了一行代码:

    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}
    {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的会员和用户朋友一直以来的支持,你的批评是我们进步的力量!欢迎加入框架的开源项目

  • 相关阅读:
    POJ 1887 Testing the CATCHER
    HDU 3374 String Problem
    HDU 2609 How many
    POJ 1509 Glass Beads
    POJ 1458 Common Subsequence
    POJ 1159 Palindrome
    POJ 1056 IMMEDIATE DECODABILITY
    POJ 3080 Blue Jeans
    POJ 1200 Crazy Search
    软件体系结构的艺术阅读笔记1
  • 原文地址:https://www.cnblogs.com/bluedoctor/p/3272993.html
Copyright © 2011-2022 走看看