一行代码调用实现带字段选取+条件判断+排序+分页功能的增强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行,目前还没有写注释,详细代码请展开看下面的内容:

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

分类:  PDF.NET


    本文转自深蓝医生博客园博客,原文链接:http://www.cnblogs.com/bluedoctor/p/3272993.html,如需转载请自行联系原作者


相关文章
|
17小时前
|
云安全 人工智能 自然语言处理
|
5天前
|
搜索推荐 编译器 Linux
一个可用于企业开发及通用跨平台的Makefile文件
一款适用于企业级开发的通用跨平台Makefile,支持C/C++混合编译、多目标输出(可执行文件、静态/动态库)、Release/Debug版本管理。配置简洁,仅需修改带`MF_CONFIGURE_`前缀的变量,支持脚本化配置与子Makefile管理,具备完善日志、错误提示和跨平台兼容性,附详细文档与示例,便于学习与集成。
309 116
|
7天前
|
数据采集 人工智能 自然语言处理
Meta SAM3开源:让图像分割,听懂你的话
Meta发布并开源SAM 3,首个支持文本或视觉提示的统一图像视频分割模型,可精准分割“红色条纹伞”等开放词汇概念,覆盖400万独特概念,性能达人类水平75%–80%,推动视觉分割新突破。
532 50
Meta SAM3开源:让图像分割,听懂你的话
|
20天前
|
域名解析 人工智能
【实操攻略】手把手教学,免费领取.CN域名
即日起至2025年12月31日,购买万小智AI建站或云·企业官网,每单可免费领1个.CN域名首年!跟我了解领取攻略吧~
|
4天前
|
人工智能 Java API
Java 正式进入 Agentic AI 时代:Spring AI Alibaba 1.1 发布背后的技术演进
Spring AI Alibaba 1.1 正式发布,提供极简方式构建企业级AI智能体。基于ReactAgent核心,支持多智能体协作、上下文工程与生产级管控,助力开发者快速打造可靠、可扩展的智能应用。
|
3天前
|
弹性计算 人工智能 Cloud Native
阿里云无门槛和有门槛优惠券解析:学生券,满减券,补贴券等优惠券领取与使用介绍
为了回馈用户与助力更多用户节省上云成本,阿里云会经常推出各种优惠券相关的活动,包括无门槛优惠券和有门槛优惠券。本文将详细介绍阿里云无门槛优惠券的领取与使用方式,同时也会概述几种常见的有门槛优惠券,帮助用户更好地利用这些优惠,降低云服务的成本。
260 132
|
7天前
|
机器学习/深度学习 人工智能 自然语言处理
AgentEvolver:让智能体系统学会「自我进化」
AgentEvolver 是一个自进化智能体系统,通过自我任务生成、经验导航与反思归因三大机制,推动AI从“被动执行”迈向“主动学习”。它显著提升强化学习效率,在更少参数下实现更强性能,助力智能体持续自我迭代。开源地址:https://github.com/modelscope/AgentEvolver
374 30
|
14天前
|
安全 Java Android开发
深度解析 Android 崩溃捕获原理及从崩溃到归因的闭环实践
崩溃堆栈全是 a.b.c?Native 错误查不到行号?本文详解 Android 崩溃采集全链路原理,教你如何把“天书”变“说明书”。RUM SDK 已支持一键接入。
698 224