本文介绍如何使用 Rafy 框架中的 Sql Tree 查询:

除了开发者常用的 Linq 查询,Rafy 框架还提供了 Sql 语法树的方式来进行查询。

这种查询方式下,开发者不需要直接编写真正的 Sql 语句,而是转而使用一套中间 Sql 语法树对象。这隔离了与具体数据库的耦合,使得开发者编写的查询可以跨越多种不同的数据库运行,甚至可以在非关系型数据库中运行。同时,框架还结合托管属性,提供了方便开发者使用的 API,并尽量保持与传统 Sql 相近的语法,使得开发者可以快速理解并编写。

本文包含以下章节:

  • 快速示例
  • 使用场景
  • 代码段
  • 更多示例

快速示例


SqlTree 查询是直接以一种类似于 Sql 语法的格式,并结合实体托管属性 IManagedProperty 来进行查询的查询模式。如下:

[RepositoryQuery]
public virtual ChapterList GetBy(string name, PagingInfo pi)
{
var f = QueryFactory.Instance; var t = f.Table<Chapter>(); var q = f.Query(
selection: f.SelectAll(),//查询所有列
from: t,//要查询的实体的表
where: t.Column(Chapter.NameProperty).Contains(name)//where 条件,
orderBy: new List<IOrderBy> {//排序
f.OrderBy(source.Column(Chapter.NameProperty), OrderDirection.Ascending)
}
); return (ChapterList)this.QueryData(q, pi);
}

可以看到,SqlTree 语法非常简单:

  • 通过 QueryFactory.Instance 类型的单例对象来定义整个 SqlTree 查询对象。
  • 查询中使用的是实体类型(Chapter)和实体的托管属性(Chapter.NameProperty)来定义表和字段。

更多的查询语法示例,见本节后面的更多示例。

 

使用场景


当您处于以下场景时,需要使用 SqlTree 查询:

  1. Linq 查询无法支持的一些场景。
    Linq 查询目前只支持有限的一些操作符的解析,以及不太复杂的关系的分析。所以当您的查询较为复杂,已经无法使用 Linq 查询来实现时,可以考虑使用 SqlTree 查询。
  2. 需要更精确地控制 Sql 语句。

    如果想要更加精确地控制最终生成的 Sql 语句,也需要使用 SqlTree。

    例如,Linq 查询中需要两个实体有确切的实体关系才会最终生成 Join 语句;但是 SqlTree 则与 Sql 语句无异,开发者可以随意将两个实体对应的表进行 Join 操作。

  3. 需要更好的性能。

    SqlTree 查询是 Rafy 框架查询数据(表格、实体)的核心实现。在框架底层,Linq 查询也都是完全是基于 SqlTree 查询来实现的。当开发者在使用 Linq 查询时,编译器其实是生成一组对象来表示一棵表达式树,而 Rafy 框架会解析这棵树,生成更加底层的 SqlTree 对象,才交给执行引擎去生成真正的 Sql 语句并最终执行。所以,直接使用 SqlTree 则节约了表达式树的生成(大量反射与对象)与解析的性能消耗。

    同样,Rafy 没有象 Hibernate 框架定义一套新的基于字符串的查询语法(如 hql),也是因为开发者编写 hql,不但无法得到编译时的语法支持,而且性能上也需要消耗对 hql 进行解析并生成 SqlTree,不如直接使用更直接的 SqlTree。

    当然,Rafy 在 SqlTree 的基础上再推出 Linq 查询的原因,是因为 SqlTree 本身需要一定的学习周期才能使用,而开发者则更熟悉使用 Linq 语法进行查询,基本可以认为是上手即用,所以支持 Linq 查询可以简化大部分的简单开发场景。

  4. 希望编写更通用的查询。

    仓库基类 EntityRepository 中自带的 GetAll、GetById 等方法,都是面向所有实体类型的非常通用的查询。对于基于 Rafy 的上层框架的开发者而言,除了直接使用这些自带的通用查询,很多时候是需要自行编写一些类似的通用查询的。

    Linq 的 Labmda 语法中的属性表达式(e.Name)需要绑定具体的实体类型(Book e),这导致了必须使用反射去生成表达式树,才能编写通条蚁。但是,SqlTree 的语法是基于托管属性框架的,它不需要使用确切的实体属性表达式,只需要使用托管属性的运行时对象 IManagedProperty 即可(Book.NameProperty)。这使得开发人员可以更加方便地编写通用查询。例如,仓库基类 EntityRepository 中的所有查询方法,都是直接通过使用实体的托管属性来实现的,例如:GetById、GetByParentId、GetAll 等。

  5. 可以为扩展属性编写查询。

    由于扩展属性写在额外的程序集插件中的,所以当无法通过 Linq 表达式进行查询。这时就不得不通过托管属性 IManagedProperty 来定义 SqlTree 完成查询了。

    关于扩展属性,参见:扩展属性

  6. 支持多个数据库。

    上述的场景中,其实还可以直接编写 Sql 语句来进行查询。但是这样就很难保证开发者编写的 Sql 语句能够在多个数据库上能够正确运行。
  7. 查询需要支持仓库数据层的扩展点。

    由于 Rafy 的查询核心都是基于 SqlTree 来实现的,所以内部的所有扩展点都是要依赖 SqlTree的。如果开发者直接编写 Sql 语句来查询,那么这些许多的扩展点都将无效,无法对开发者编写的这条 Sql 语句进行扩展。

    例如:当使用 幽灵插件 对所有幽灵数据进行自动过滤时,如果开发者使用手工编写的 Sql 语法进行查询,那么自动过滤功能无效,需要开发者自己进行幽灵数据的过滤。

代码段


RafySDK 中提供了两个代码段,来辅助开发者生成基本的 SqlTree 查询结构:Rafy_Query、Rafy_Query_TableQueryContent。

详情见:代码段

更多示例


下面将会列出一些常见的 SqlTree 查询示例。通过这些代码,您将学习到如何在各种查询需求下使用 SqlTree。

基础查询:

[RepositoryQuery]
public virtual ChapterList GetBy(string name, PagingInfo pi)
{
var f = QueryFactory.Instance; var t = f.Table<Chapter>(); var q = f.Query(
//selection: f.SelectAll(),//没有 selection,则默认表示查询所有列
from: t,//要查询的实体的表
where: t.Column(Chapter.NameProperty).Contains(name)//where 条件
); return (ChapterList)this.QueryData(q, pi);
}

表格数据查询:

[RepositoryQuery]
public virtual LiteDataTable GetBy(string name, PagingInfo pi)
{
var f = QueryFactory.Instance; var t = f.Table<Chapter>(); var q = f.Query(
from: t,
where: t.Column(Chapter.NameProperty).Contains(name)
); return this.QueryTable(q, pi);//由查询实体变为查询数据表格,只是更换了这一行代码。
} 两个列的条件进行比较:
var table = f.Table(this);//使用当前的仓库来表示当前的表
var q = f.Query(
from :table,
where: table.Column(Chapter.NameProperty).Equal(table.Column(Chapter.CodeProperty))//两个列相等
); 使用 And、Or:
var table = f.Table(this);
var q = f.Query(
from :table,
where: f.And(
table.Column(Chapter.NameProperty).Equal(name),
f.Or(
table.Column(Chapter.IdProperty).LessEqual(10),
table.Column(Chapter.IdProperty).GreaterEqual(1000)
)
)
);

Join(SerialNumberValueRepository 中的真实代码):

/// <summary>
/// 获取某个规则下最新的一个值。
/// </summary>
/// <param name="autoCodeName"></param>
/// <returns></returns>
[RepositoryQuery]
public virtual SerialNumberValue GetLastValue(string autoCodeName)
{
var f = QueryFactory.Instance;
var t = f.Table<SerialNumberValue>();
var t2 = f.Table<SerialNumberInfo>();
var q = f.Query(
from: t.Join(t2),//由于 SerialNumberValue 有一个 SerialNumberInfo 的引用属性,则在使用 Join 时,不需要给出 Join 的条件。
where: t2.Column(SerialNumberInfo.NameProperty).Equal(autoCodeName),
orderBy: new List<IOrderBy> { f.OrderBy(t.Column(SerialNumberValue.LastUpdatedTimeProperty), OrderDirection.Descending) }
); return (SerialNumberValue)this.QueryData(q);
}

使用完整的 Join:

var t = f.Table<SerialNumberValue>();
var t2 = f.Table<SerialNumberInfo>();
var q = f.Query(
from: t.Join(t2, t.Column(SerialNumberValue.SerialNumberInfoIdProperty).Equal(t2.Column(SerialNumberInfo.IdProperty)), JoinType.Inner),//不但可以给出具体的 Join 条件,还可以给出 Join 类型。
where: t2.Column(SerialNumberInfo.NameProperty).Equal(autoCodeName),
orderBy: new List<IOrderBy> { f.OrderBy(t.Column(SerialNumberValue.LastUpdatedTimeProperty), OrderDirection.Descending) }
);

Exists:

var bookTable = f.Table(this);
var chapterTable = f.Table<Chapter>();
var q = f.Query(
from: bookTable,
where: f.Exists(f.Query(
from: chapterTable,
where: chapterTable.Column(Chapter.BookIdProperty).Equal(bookTable.IdColumn)
))
);

Not Exists:

var book = f.Table(this);
var chapter = f.Table<Chapter>();
var q = f.Query(
from: book,
where: f.Not(f.Exists(f.Query(
from: chapter,
where: f.And(
f.Constraint(chapter.Column(Chapter.BookIdProperty), book.IdColumn),
f.Constraint(chapter.Column(Chapter.NameProperty), PropertyOperator.NotEqual, chapterName)
)
)))
);

更多示例,请参照源码中单元测试的 ORMTest 中的 TableQuery 相关方法。

PS:该文已经纳入《 Rafy 用户手册》中。


Rafy 框架 - 使用 SqlTree 查询的更多相关文章

  1. Rafy 框架 - 通用查询条件(CommonQueryCriteria)

    在应用开发过程中,有 80% 的场景下,开发者所需要的实体查询,查询条件中其实都是一些简单的属性匹配,又或是一些属性匹配的简单组合.Rafy 为这样的场景提供了更为方便使用的 API:CommonQu ...

  2. Rafy 框架 - 幽灵插件(假删除)

      Rafy 框架又添新成员:幽灵插件.本文将解释该插件的场景.使用方法.原理.   场景 在开发各类数据库应用系统时,往往需要在删除数据时不是真正地删除数据,而只是把数据标识为'已删除'状态.这些数 ...

  3. Rafy 中的 Linq 查询支持(根据聚合子条件查询聚合父)

    为了提高开发者的易用性,Rafy 领域实体框架在很早开始就已经支持使用 Linq 语法来查询实体了.但是只支持了一些简单的.常用的条件查询,支持的力度很有限.特别是遇到对聚合对象的查询时,就不能再使用 ...

  4. 快速开发~Rafy框架的初步认识

    当我们开始使用EF的同时,是不是就会更好的认识了其他的ORM框架,最近接触了Rafy的使用,感觉还是蛮有兴趣去学习的,虽然最初的我到现在看的并不深入,但是我个人感觉还是可以简单地做一些总结的啦,或许语 ...

  5. Rafy 框架 - 时间戳插件

    本文将解释 Rafy 框架中的时间戳插件的场景.使用方法.原理. 场景 在开发各类数据库应用系统时,业务领域实体往往需要包含"创建时间"."最后更新时间".&q ...

  6. Rafy框架

    l  什么是Rafy框架? -------- Rafy 是一个面向企业级开发的插件化快速开发框架. l  Rafy的优点是什么? ------快速开发.产品线工程.一套代码可同时生成并运行 C/S.单 ...

  7. Rafy 框架 - 实体支持只更新部分变更的字段

    Rafy 快一两年没有大的更新了.并不是这个框架没人维护了.相反,主要是因为自己的项目.以及公司在使用的项目,都已经比较稳定了,也没有新的功能添加.但是最近因为外面使用了 Rafy 的几个公司,找到我 ...

  8. Rafy 框架:领域控制器

    本文简要说明如何使用 Rafy 框架中的领域控制器. 简介 领域控制器是 Rafy 框架中用于封装领域逻辑的主要方式. 在控制器中,开发者可以封装大量的业务逻辑,并向外暴露业务接口.内部的逻辑在实现时 ...

  9. 使用 NuGet 下载最新的 Rafy 框架及文档

    为了让开发者更方便地使用 Rafy 领域实体框架,本月,我们已经把最新版本的 Rafy 框架程序集发布到了 nuget.org 上,同时,还把 RafySDK 的最新版本发布到了 VisualStud ...

随机推荐

  1. 红黑树——算法导论(15)

    1. 什么是红黑树 (1) 简介     上一篇我们介绍了基本动态集合操作时间复杂度均为O(h)的二叉搜索树.但遗憾的是,只有当二叉搜索树高度较低时,这些集合操作才会较快:即当树的高度较高(甚至一种极 ...

  2. 120项改进:开源超级爬虫Hawk 2.0 重磅发布!

    沙漠君在历时半年,修改无数bug,更新一票新功能后,在今天隆重推出最新改进的超级爬虫Hawk 2.0! 啥?你不知道Hawk干吗用的? 这是采集数据的挖掘机,网络猎杀的重狙!半年多以前,沙漠君写了一篇 ...

  3. RabbitMq应用一

    RabbitMq应用一 RabbitMQ的具体概念,百度百科一下,我这里说一下我的理解,如果有少或者不对的地方,欢迎纠正和补充. 一个项目架构,小的时候,一般都是传统的单一网站系统,或者项目,三层架构 ...

  4. Oracle学习之路-- 案例分析实现行列转换的几种方式

    注:本文使用的数据库表为oracle自带scott用户下的emp,dept等表结构. 通过一个例子来说明行列转换: 需求:查询每个部门中各个职位的总工资 按我们最原始的思路可能会这么写:       ...

  5. javascript设计模式:策略模式

    前言 策略模式有效利用组合.委托.多态等技术和思想,可以有效避免多重条件选择语句. 策略模式对开放-封闭原则提供了很好的支持,将算法封装在strategy中,使得他们易于切换.理解.扩展. 策略模式中 ...

  6. 微信小程序中利用时间选择器和js无计算实现定时器(将字符串或秒数转换成倒计时)

    转载注明出处 改成了一个单独的js文件,并修改代码增加了通用性,点击这里查看 今天写小程序,有一个需求就是用户选择时间,然后我这边就要开始倒计时. 因为小程序的限制,所以直接选用时间选择器作为选择定时 ...

  7. CommandPattern

    /** * 命令模式 * @author TMAC-J * 将调用者和接受者分离 * 可以将一组命令组合在一起,适合很多命令的时候 */ public class CommandPattern { i ...

  8. 分布式学习系列【dubbo入门实践】

    分布式学习系列[dubbo入门实践] dubbo架构 组成部分:provider,consumer,registry,monitor: provider,consumer注册,订阅类似于消息队列的注册 ...

  9. 在 SharePoint Server 2016 本地环境中设置 OneDrive for Business

    建议补丁 建议在sharepoint2016打上KB3127940补丁,补丁下载地址 https://support.microsoft.com/zh-cn/kb/3127940 当然不打,也可以用O ...

  10. Linux基础介绍【第一篇】

    Linux简介 什么是操作系统? 操作系统,英文名称Operating System,简称OS,是计算机系统中必不可少的基础系统软件,它是应用程序运行以及用户操作必备的基础环境支撑,是计算机系统的核心 ...