曙光: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>();

 

  目的达到了,原来只要肯想法,办法还是很简单的,心中一阵窃喜:)

一、SQL与ORM

   关系数据库(RDBMS)的查询有SQL(Structured Query Language)结构化查询语言,相比高级程序语言(命令式语言)而言,SQL主要描述想要做什么,而不是命令式语言的具体如何做,因此,SQL也被称为第四代语言(4GL),它为现代大多数的关系数据库系统所支持。SQL的核心是对“关系”的操作,数据库理论研究证明,SQL是关系上完备的,但是当代大多数高级语言都是面向对象的,高级语言程序要跟关系数据库进行交互,SQL就成了必须的桥梁,由于SQL基于的“关系”和程序语言的“对象”是不同的体系,它们之间要完成很好的交互,就得有一个“映射”过程,实现这个过程的程序,就是ORM(Object/Relation Mapping)。

  应用程序调用ORM的方法,ORM自动生成相应的SQL语句到数据库进行查询,然后ORM将接收到的关系数据映射成实体对象。如果没有使用ORM,那么通常应用程序会拆分出一个数据访问层(DAL)来生成SQL语句并执行相应的查询。所以,ORM出现后,在一定程度上,它可以取代DAL,这使得你少了一个层的工作量,对于提高工作效率是很重要的。

  下图是应用程序使用ORM和使用传统的DAL的一个示意图。

图片 1

 

 

(图1:两种数据访问架构)

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

巴科斯范式的内容

  在双引号中的字(“word”)代表着这些字符本身。而double_quote用来代表双引号。

 

  在双引号外的字(有可能有下划线)代表着语法部分。

 

  尖括号( < > )内包含的为必选项。

 

  方括号( [ ] )内包含的为可选项。

 

  大括号( { } )内包含的为可重复0至无数次的项。

 

  竖线( | )表示在其左右两边任选一项,相当于”OR”的意思。

 

  ::= 是“被定义为”的意思。

精简:让用户再懒一点

  过了一会儿,再反复看看上面这一行代码,发现了几个问题:

  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行,目前还没有写注释,详细代码请展开看下面的内容:

图片 2图片 3

 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详细代码–

2,PDF.NET的ORM框架

  PDF.NET的ORM框架包括4个部分:

  1. Entity Object :PDF.NET实体类,它继承于基类
    EntityBase,使得每一个实体类都成为一个“数据容器”;
  2. OQL:ORM查询语言,以实体类对象为操作对象,生成查询表达式,供实体查询对象使用。
  3. AdoHelper:数据访问提供程序抽象类,封装了对ADO.NET的各种访问,包括事物操作;框架默认提供了OledbProvider、OdbcProvider、AccessProvider、SqlServerProvider、OracleProvider等,要支持更多的数据库,只需要继承AdoHelper即可。
  4. EntityQuery<T> :实体查询对象,它是一个O/R
    Mapping对象,它操作涉及的对象类型是一个实体类(类型T);在对象内部,它会把OQL转换成SQL,然后调用AdoHelper完成查询。

 

图片 4 

 (图2:PDF.NET OQL 架构)

 

  如果仅从查询调用端来观察,我们发现OQL,跟SQL逻辑上是等价的,一个是“对象化”的查询,一个是“结构化”的查询:

 

 

对象化查询:OQL->ORM-> Entity Objects

等于

结构化查询:SQL ->DB-> DataSet

 

  如果最终效果Entity
Object==DataSet,那么OQL==SQL。

  所以,OQL的设计目标,就是要它生成的SQL语句效果基本达到手写的SQL语句一样。由于SQL的具体实现又有很多不同的版本,所以很多时候SqlServer用的SQL语句在Oracle 上不一定能够使用,只有那些完全标准的SQL语句才是通用的,因此,OQL的设计,也必须是这样标准的SQL规范,目前,实现的是SQL92标准规范。

 成功:一行代码的真相

 

为了让大家更清楚GOQL的结构和它与PDF.NET框架其它部分的关系,请看下面的类图:

-类图-

图片 5

  最后,我们就可以写一个真正的测试代码了:
 
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的实体类

 

  有图有真相,下面是这个测试程序的输出截图:

-截图-

图片 6

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

ORM查询语言(OQL)简介–概念篇

ORM查询语言(OQL)简介–实例篇

3,OQL查询范式

  下面是OQL支持的查询范式举例,注意下面的定义里面使用了“BNF”范式,为了避免大家误会,这里补充下BFN的内容,详细内容请参考这个链接:http://baike.baidu.com/view/1137652.htm

 

ORM查询语言(OQL)简介–高级篇:脱胎换骨

 相关文章内容索引:

 1,分离关注点

  那么,这些问题ORM能够解决吗?ORM本来是完成“对象-关系映射”的,但这里大多数的ORM都包含了“生成SQL”的功能,而要实现SQL那样的灵活性,那么我们必须分离出ORM这个关注点,将“生成SQL”的功能从ORM中抽取出来,这样我们就能够有更多的精力致力于发明一个面向对象的,用于ORM查询的语言,(ORM Query
Language) ,这就是OQL。

 

  ORM查询语言,其实早就有了,从早期的Hibernate的HQL,到MS的Linq(Linq2SQL,EF其实内部都是使用Linq生成的SQL),它们都可以生成复杂的SQL语句,它们都是直接作用于ORM框架的。几乎在与Linq同一时期,PDF.NET也发明了自己的ORM查询语言,称为OQL。下面提到的OQL,都是指的PDF的OQL。

 

4,数据删除:

 

OQL q=OQL.From(entityObject)

.Delete()

.Where([<entityObject.Property1>[,entityObject.Property2][,…]]|[OQL2]|[OQLCompare])

.End;

 

下篇我们将使用实例来讲解OQL的具体使用,敬请期待。 

 

 

注:PDF.NET现在已经开源,有关框架的详细信息,请看官网介绍:http://www.pwmis.com/sqlmap

 

 开源信息介绍:

节前送礼:PDF.NET(PWMIS数据开发框架)V4.5版开源

 

———————分界线—————————————–

文章发布后,热心网友
对我提了一些意见,由于不涉及个人隐私,这里贴上他的原文,如果他看到有异议的话请及时跟我联系。在此感谢他提出的下列意见!

 

另外大家觉得好请点个推荐,反对的话请留个理由,谢谢。

———————分界线—————————————–

 

 shawn(630235793) 1:10:46
大体浏览了一下,感觉还不错

shawn(630235793) 1:14:32
只是博文过于技术细节化而缺少了框架解决的具体问题域、面向的使用者类型,以及整体架构思想与基于关系数据访问框架的差异性描述,让读者一上来很难理解ORM框架的意图。

回复:

这些问题的确没有表述清楚,也是因为我的撰文水平有限,没有想到这些问题,也不知道该怎样来表述。

PDF.NET的OQL要解决的主要问题就是让ORM操作能够有SQL那样的灵活性,现有大多数ORM框架都是基于CRUD方法级别的操作,还没有像SQL那样具有语言级别的操作,要不然它怎么会被称为4GL呢?现在,我觉得LINQ也具有了这样的能力,而我框架中的OQL,也有这样的能力,所以我大胆的称呼它是一个“ORM
Query
Laguage”,就像SQL是提供给RDBMS的查询引擎使用一样,OQL是提供给ORM使用的。

所以,OQL面向的使用者是那些喜欢ORM方式来访问数据库,又喜欢SQL的灵活性的技术人员,或者是提供给喜欢其中一种(ORM或者SQL)而不太喜欢另外一种方式的人,让他们有机会体会到另一种方式的优势。

整体思想就是,用面向对象的方式来操作数据库,用OO的方式来写SQL!

PS:OQL与LINQ相比,它更接近于SQL风格,用惯了SQL的人,第一次接触LINQ是很不习惯的,至少我是如此。

 

shawn(630235793) 2012-10-6 1:39:15
数据访问框架设计的初始设想,首先应该是满足调用层的使用要求,换句话说请求是事务性的,还是非事务性的。如果用户的请求是事务性的,在访问层应该提供事务性的处理机制。而不是应用层自己来对是否事务性进行处理。这些应该放在访问层的对外交互接口处提供给用户来选择比较合理。
所以,框架内部的分层,我感觉还应该再多考虑一下比较好。

回复:

实体层的接口是有的,只是这个图里面不好放置而且不是重点,省略了。
是否使用事物,是放在访问层的对外交互接口处提供给用户来选择的。

shawn(630235793) 2012-10-6 1:47:13
毕竟数据访问框架对于用户来讲就应该屏蔽所有数据库之间操作的差异性,所有与数据库相关的一切操作都封装于内部。对于用户来讲这些都是完全不必去考虑的,只需要提出具体请求是什么就可以了。对于如何解读用户请求、如何根据用户选择的具体数据库,而将请求翻译成底层数据库操作指令等等,这些都是访问层内部机制完成的。

 

回复:

正如你所说,框架正是这样去做的,OQL屏蔽了SQL不同数据库之间的差异,它会根据具体使用的数据库,去生成本地化的SQL。

 

 

广州-海華²º¹²<harvey.cai@qq.com>  17:04:10
跟 linq 有什么相似/区别?
广州-海華²º¹²<harvey.cai@qq.com>  17:05:45
这篇博文里主推的理念,让人想到 linq。

pdf.net 主推的应该是:linq 般好用,但是性能卓越

 

  

回复:

 

LINQ是.NET独有的特性,“语言集成查询”,它是集成在.NET语言中的,这是它的先天优势。LINQ基于表达式树,所以它要求必须是.NET平台而且框架版本要求在.NET3.5及以上。

PDF.NET的OQL跟LINQ一样都是ORM框架使用的语言,但是OQL语法更接近于SQL,很容易上手,而且,OQL没有使用.NET的高级特性,这使得它只要是面向对象的语言而且支持泛型即可实现,因此将它移植到C++、Java是完全可能的,而且在.NET平台上,它也仅需.NET2.0版本的支持。

 

 

 

 

3,数据更新:

 

OQL q=OQL.From(entityObject)

.Update([entityObject.Property1][,entityObject.Property2][{,…}])

.Where([<entityObject.Property1>[,entityObject.Property2][,…]]|[OQL2]|[OQLCompare])

.End;

 

1,数据查询:

OQL q=OQL.From(entityObject)

[.[InnerJoin|LeftJoin|RightJoin](entityObject2).On(entityObject.PK,entityObject2.FK)]

[.[InnerJoin|LeftJoin|RightJoin](entityObject3).On(entityObject.PK,entityObject3.FK)]

.Select([entityObjectX.Property1][,entityObjectX.Property2][{,…}])

.Where([<entityObject.Property1>[,entityObject.Property2][,…]]|[OQL2]|[OQLCompare])

.GroupBy(entityObjectX.PropertyN,<“asc”>|<“desc”>)

.HavingBy(entityObjectX.PropertyM)

.End;

 

 如果需要分页,仅需要这样操作:

q.Limit(分页大小[,页码[,总记录数]]);

 

 执行该方法,会生成特定数据库平台的分页SQL语句。

 

 二、ORM带来的问题

 

 

  使用ORM后,再也不用去写那些枯燥的DAL代码了,不用拼接那些可能存在安全问题或者敲错字段名的SQL语句,但是我们发现,仅仅使用ORM它反而丧失了SQL的灵活性,这也是不少人拒绝使用ORM的理由。我们看看很多人的ORM是怎么定义数据操作接口的,他们常常把这些接口方法由实体类去实现,从而制造一个个充血的实体类:

public interface IEntity<T> where T:class
{
void Add();
void Update();
void Delete();
List<T> GetAll();
T GetOne(int id);
}

 

  使用充血实体类,在使用上还是比较方便的,但是Insert
\Update\Delete 都只能操作一条数据,GetAll 方法不仅仅把全部数据拿出来了,而且还可能把不需要的字段值也拿了出来,那个GetOne方法,也许实际上表的主键并不是int
类型的,那么这些定义就会有问题。。。

  所以,我们见到很多使用了ORM的项目,不管数据是否全部需要,先拿出来再说,不管主键是不是int
类型,先定一个方法在那里,大不了是个空方法,不管当前实体是否需要Delete功能(比如某些系统用户数据是不能删除的),都基类给直接实现了。。。。。。

2,数据统计:

 

OQL q=OQL.From(entityObject)

.Select().Count(entityObject.PropertyX,<””>|<“CountAsName”>)

.Where([<entityObject.Property1>[,entityObject.Property2][,…]]|[OQL2]|[OQLCompare])

.End;

 

三、ORM查询语言

ORM查询语言(OQL)简介–高级篇(续):庐山真貌

相关文章