Rafy 中的 Linq 查询支持(根据聚合子条件查询聚合父)
为了提高开发者的易用性,Rafy 领域实体框架在很早开始就已经支持使用 Linq 语法来查询实体了。但是只支持了一些简单的、常用的条件查询,支持的力度很有限。特别是遇到对聚合对象的查询时,就不能再使用 Linq,而只能通过构造底层查询树的接口来完成了。由于开发者的聚合查询的需求越来越多,所以本周我们将这部分进行了增强。
接下来,本文将说明 Rafy 框架原来支持的 Linq 语法,以及最新加入的聚合查询支持及用法。
使用 Linq 查询的代码示例
public WarehouseList GetByCode(string warehouseCode, string nameKeywords, PagingInfo pagingInfo)
{
return this.FetchList(r => r.DA_GetByCode(warehouseCode, nameKeywords, pagingInfo));
}
private EntityList DA_GetByCode(string warehouseCode, string nameKeywords, PagingInfo pagingInfo)
{
var q = this.CreateLinqQuery();
//条件对比
q = q.Where(e => e.Code == warehouseCode);
if (!string.IsNullOrEmpty(nameKeywords))
{
q = q.Where(e => e.Name.Contains(nameKeywords));
}
//排序
q = q.OrderByDescending(w => w.Name);
return this.QueryList(q, pagingInfo);//以指定的分页信息 pagingInfo 分页
}
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
支持的一般查询
使用 CreateLinqQuery 方法创建出一个 IQueryable<Warehouse> 对象,针对该对象,我们可以以下的标准 Linq 方法:Where、OrderBy、OrderByDescending、ThenBy、ThenByDescending、Count。
对于其中最重要的 Where 方法,Rafy 也支持许多操作,包括:
- 属性的各种对比操作(=,!=,>,>=,<,<=,!,Contains,StartsWith,EndsWith等)。
- 支持两个属性条件间的连接条件:&&、||。
- 支持引用查询。即间接使用引用实体的属性来进行查询,在生成 Sql 语句时,将会生成 INNER JOIN 语句,连接上这些被使用的引用实体对应的表。例如:
q = q.Where(warehouse => warehouse.Administrator.Name == "admin");
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
这部分的内容,之前的版本已经支持了,各位可参见 Rafy 框架的用户手册。
聚合查询
聚合查询的功能是,开发者可以通过定义聚合子的属性的条件,来查询聚合父。这是本次升级的重点。
例如,书籍管理系统中,Book (书)为聚合根,它拥有 Chapter (章)作为它的聚合子实体,而 Chapter 下则还有 Section(节)。那么,我们可以通过这个功能,来查询类似以下需求的数据:
- 查询拥有某个章的名字的所有书籍。
要实现这种场景的查询,我们可以在仓库的数据层,使用下面的 Linq 语法:
public BookList LinqGetIfChildrenExists(string chapterName)
{
return this.FetchList(r => r.DA_LinqGetIfChildrenExists(chapterName));
}
private EntityList DA_LinqGetIfChildrenExists(string chapterName)
{
var q = this.CreateLinqQuery();
q = q.Where(book => book.ChapterList.Concrete().Any(c => c.Name == chapterName));
q = q.OrderBy(b => b.Name);
return this.QueryList(q);
}
其生成的 Sql 如下:
SELECT [T0].[Id], [T0].[Author], [T0].[BookCategoryId], [T0].[BookLocId], [T0].[Code], [T0].[Name], [T0].[Price], [T0].[Publisher]
FROM [Book] AS [T0]
WHERE EXISTS (
SELECT 1
FROM [Chapter] AS [T1]
WHERE [T1].[BookId] = [T0].[Id] AND [T1].[Name] = @p0
)
ORDER BY [T0].[Name] ASC
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
- 查询每个章的名字必须满足某条件的所有书籍。
我们可以在仓库的数据层,使用下面的 Linq 语法:
public BookList LinqGetIfChildrenAll(string chapterName)
{
return this.FetchList(r => r.DA_LinqGetIfChildrenAll(chapterName));
}
private EntityList DA_LinqGetIfChildrenAll(string chapterName)
{
var q = this.CreateLinqQuery();
q = q.Where(e => e.ChapterList.Cast<Chapter>().All(c => c.Name == chapterName));
q = q.OrderBy(e => e.Name);
return this.QueryList(q);
}
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }生成的 SQL 是:
SELECT [T0].[Id], [T0].[Author], [T0].[BookCategoryId], [T0].[BookLocId], [T0].[Code], [T0].[Name], [T0].[Price], [T0].[Publisher]
FROM [Book] AS [T0]
WHERE NOT (EXISTS (
SELECT 1
FROM [Chapter] AS [T1]
WHERE [T1].[BookId] = [T0].[Id] AND [T1].[Name] != @p0
))
ORDER BY [T0].[Name] ASC
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
- 查询某个章中所有节必须满足某条件的所有书籍。
我们可以在仓库的数据层,使用下面的 Linq 语法:
public BookList LinqGetIfChildrenExistsSectionName(string sectionName)
{
return this.FetchList(r => r.DA_LinqGetIfChildrenExistsSectionName(sectionName));
}
private EntityList DA_LinqGetIfChildrenExistsSectionName(string sectionName)
{
var q = this.CreateLinqQuery();
q = q.Where(book => book.ChapterList.Concrete().Any(c => c.SectionList.Cast<Section>().Any(s => s.Name.Contains(sectionName))));
q = q.OrderBy(b => b.Name);
return this.QueryList(q);
}
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
将会生成如下 SQL:
SELECT [T0].[Id], [T0].[Author], [T0].[BookCategoryId], [T0].[BookLocId], [T0].[Code], [T0].[Name], [T0].[Price], [T0].[Publisher]
FROM [Book] AS [T0]
WHERE EXISTS (
SELECT 1
FROM [Chapter] AS [T1]
WHERE [T1].[BookId] = [T0].[Id] AND EXISTS (
SELECT 1
FROM [Section] AS [T2]
WHERE [T2].[ChapterId] = [T1].[Id] AND [T2].[Name] LIKE @p0
)
)
ORDER BY [T0].[Name] ASC
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
- 同时,这些查询也可以支持分页。例如,我们在上面的查询添加一个分页条件,代码如下:
public BookList LinqGetIfChildrenExistsSectionName(string sectionName)
{
return this.FetchList(r => r.DA_LinqGetIfChildrenExistsSectionName(sectionName));
}
private EntityList DA_LinqGetIfChildrenExistsSectionName(string sectionName)
{
var q = this.CreateLinqQuery();
q = q.Where(book => book.ChapterList.Concrete().Any(c => c.SectionList.Cast<Section>().Any(s => s.Name.Contains(sectionName))));
q = q.OrderBy(b => b.Name);
return this.QueryList(q, new PagingInfo(2, 1));//分页
}
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
分成的 SQL 如下:
SELECT TOP 1 [T0].[Id], [T0].[Author], [T0].[BookCategoryId], [T0].[BookLocId], [T0].[Code], [T0].[Name], [T0].[Price], [T0].[Publisher]
FROM [Book] AS [T0]
WHERE EXISTS (
SELECT 1
FROM [Chapter] AS [T1]
WHERE [T1].[BookId] = [T0].[Id] AND EXISTS (
SELECT 1
FROM [Section] AS [T2]
WHERE [T2].[ChapterId] = [T1].[Id] AND [T2].[Name] LIKE @p0
)
) AND [T0].[Id] NOT IN (
SELECT TOP 1 [T0].[Id]
FROM [Book] AS [T0]
WHERE EXISTS (
SELECT 1
FROM [Chapter] AS [T1]
WHERE [T1].[BookId] = [T0].[Id] AND EXISTS (
SELECT 1
FROM [Section] AS [T2]
WHERE [T2].[ChapterId] = [T1].[Id] AND [T2].[Name] LIKE @p1
)
)
ORDER BY [T0].[Name] ASC
)
ORDER BY [T0].[Name] ASC
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
头晕,越来越复杂……不过经过测试,上面都没有什么问题。
下面是一个单元测试生成的分页、复杂聚合查询的 SQL,贴上来观赏下:
SELECT TOP 2 [T0].[Id], [T0].[Author], [T0].[BookCategoryId], [T0].[BookLocId], [T0].[Code], [T0].[Name], [T0].[Price], [T0].[Publisher]
FROM [Book] AS [T0]
LEFT OUTER JOIN [BookCategory] AS [T1] ON [T0].[BookCategoryId] = [T1].[Id]
WHERE [T0].[Name] != @p0 AND [T1].[Name] = @p1 AND EXISTS (
SELECT 1
FROM [Chapter] AS [T2]
WHERE [T2].[BookId] = [T0].[Id] AND [T2].[Name] = @p2
) AND EXISTS (
SELECT 1
FROM [Chapter] AS [T3]
WHERE [T3].[BookId] = [T0].[Id] AND [T3].[Name] = @p3 AND NOT (EXISTS (
SELECT 1
FROM [Section] AS [T4]
LEFT OUTER JOIN [SectionOwner] AS [T5] ON [T4].[SectionOwnerId] = [T5].[Id]
WHERE [T4].[ChapterId] = [T3].[Id] AND ([T4].[Name] NOT LIKE @p4 OR [T4].[SectionOwnerId] IS NULL OR [T5].[Name] != @p5)
))
) AND [T0].[Id] NOT IN (
SELECT TOP 4 [T0].[Id]
FROM [Book] AS [T0]
LEFT OUTER JOIN [BookCategory] AS [T1] ON [T0].[BookCategoryId] = [T1].[Id]
WHERE [T0].[Name] != @p6 AND [T1].[Name] = @p7 AND EXISTS (
SELECT 1
FROM [Chapter] AS [T2]
WHERE [T2].[BookId] = [T0].[Id] AND [T2].[Name] = @p8
) AND EXISTS (
SELECT 1
FROM [Chapter] AS [T3]
WHERE [T3].[BookId] = [T0].[Id] AND [T3].[Name] = @p9 AND NOT (EXISTS (
SELECT 1
FROM [Section] AS [T4]
LEFT OUTER JOIN [SectionOwner] AS [T5] ON [T4].[SectionOwnerId] = [T5].[Id]
WHERE [T4].[ChapterId] = [T3].[Id] AND ([T4].[Name] NOT LIKE @p10 OR [T4].[SectionOwnerId] IS NULL OR [T5].[Name] != @p11)
))
)
ORDER BY [T0].[Name] ASC
)
ORDER BY [T0].[Name] ASC
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
刚开始支持 Linq 查询的时候,就已经把聚合查询的单元测试给写了。鉴于比较复杂,所以一直没有实现。这周总算完成了这部分代码,心中一块石头落了地。
Rafy 中的 Linq 查询支持(根据聚合子条件查询聚合父)的更多相关文章
- query_string查询支持全部的Apache Lucene查询语法 低频词划分依据 模糊查询 Disjunction Max
3.3 基本查询3.3.1词条查询 词条查询是未经分析的,要跟索引文档中的词条完全匹配注意:在输入数据中,title字段含有Crime and Punishment,但我们使用小写开头的crime来搜 ...
- SEC6 - MySQL 查询语句--------------进阶2:条件查询
# 进阶2:条件查询 /* 语法: select 查询列表 from 表名 where 筛选条件; 分类: 一.按照条件表达式筛选 条件运算符:> < = !=(等价于<>) ...
- 高级查询子条件查询filter
Filter Context 在查询过程中,只判断该文档是否满足条件,只有Yes或者No { "query":{ "bool":{ //布尔关键词 " ...
- 基于Solr的HBase多条件查询测试
背景: 某电信项目中采用HBase来存储用户终端明细数据,供前台页面即时查询.HBase无可置疑拥有其优势,但其本身只对rowkey支持毫秒级 的快 速检索,对于多字段的组合查询却无能为力.针对HBa ...
- thinkphp where()条件查询
今天来给大家讲下查询最常用但也是最复杂的where方法,where方法也属于模型类的连贯操作方法之一,主要用于查询和操作条件的设置.where方法的用法是ThinkPHP查询语言的精髓,也是Think ...
- Spring MVC和Spring Data JPA之按条件查询和分页(kkpaper分页组件)
推荐视频:尚硅谷Spring Data JPA视频教程,一学就会,百度一下就有, 后台代码:在DAO层继承Spring Data JPA的PagingAndSortingRepository接口实现的 ...
- 2017.2.21 activiti实战--第十三章--流量数据查询与跟踪(一)查询接口介绍及运行时数据查询
学习资料:<Activiti实战> 第十三章 流量数据查询与跟踪 本章讲解运行时与历史数据的查询方法.主要包含三种:标准查询,Native查询,CustomSql查询. 13.1 Quer ...
- EasyUI ComboGrid的绑定,上下键和回车事件,输入条件查询
首先我们先看一下前台的绑定事件 1.先定义标签 <input id="cmbXm" type="text" style="width: 100p ...
- WebFrom 小程序【条件查询】
实现按照各种条件对数据库进行综合查询 基本功能:可以根据用户需要灵活查询 重难点:各种条件的可能.限制. public List<users> selectA( string str,Ha ...
随机推荐
- asp.net identity UserSecurityStamp 的作用
UserSecurityStamp 主要是用来对用户安全相关信息做一个快照. 在使用asp.net identity 的 CreateAsync(TUser user) 创建一个用户的时候,如果开启了 ...
- PHP是怎么运行的
这篇文章,研究一下PHP代码是如何解释和执行以及PHP脚本运行的生命周期. 概述 PHP服务的启动.严格来说,PHP的相关进程是不需要手动启动的,它是随着Apache的启动而运行的.当然,如果有需要重 ...
- 利用history实现无刷新跳转界面
看标题是不是感觉很拽的样子,其实没什么啦,也就是时下常说的单页面应用.这种web形式在如今的移动端十分流行,毕竟在移动端频繁得去刷新界面不是很友好,而且还费流量.今天我们要做一个小的app(移动端), ...
- WPF - 属性系统 (1 of 4)
本来我希望这一系列文章能够深入讲解WPF属性系统的实现以及XAML编译器是如何使用这些依赖项属性的,并在最后分析WPF属性系统的实际实现代码.但是在编写的过程中发现对WPF属性系统代码的讲解要求之前的 ...
- 基于存储过程的MVC开源分页控件--LYB.NET.SPPager
摘要 现在基于ASP.NET MVC的分页控件我想大家都不陌生了,百度一下一大箩筐.其中有不少精品,陕北吴旗娃杨涛大哥做的分页控件MVCPager(http://www.webdiyer.com/)算 ...
- WPF布局之让你的控件随着窗口等比放大缩小,适应多分辨率满屏填充应用
一直以来,我们设计windows应用程序,都是将控件的尺寸定好,无论窗体大小怎么变,都不会改变,这样的设计对于一般的应用程序来说是没有问题的,但是对于一些比较特殊的应用,比如有背景图片的,需要铺面整个 ...
- 到底应该选择那种Linux.NET的部署方式?
当前部署Linux.NET环境的方式可谓是五花八门,既有传统的源码编译的方式.又有各式各样的一键安装脚本.还有绿色包安装方式,而随着Mono官方的新站上线,更增加了采用RPM包的部署方式.那对于一名L ...
- Highchart.js
Highcharts所有的源代码及例子均可通过官网下载得到.初学者只需要简单的修改官方提供的例子即可了解Highcharts. 下载 最新版本:Highcharts 3.0.10.Highstock ...
- SQL Server中CTE的另一种递归方式-从底层向上递归
SQL Server中的公共表表达式(Common Table Expression,CTE)提供了一种便利的方式使得我们进行递归查询.所谓递归查询方便对某个表进行不断的递归从而更加容易的获得 ...
- 关于HTML5 Audio线程问题
移动端果然很坑! 在移动端IOS平台上用new Audio每次调用这个API都会创建一个新的线程,而且还不能销毁...直到拖死应用 后来改进了换了AudioContext,线程问题解决了 https: ...