博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Rafy 中的 Linq 查询支持(根据聚合子条件查询聚合父)
阅读量:7087 次
发布时间:2019-06-28

本文共 6785 字,大约阅读时间需要 22 分钟。

为了提高开发者的易用性,在很早开始就已经支持使用 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 分页
}
 

支持的一般查询


使用 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");

 

这部分的内容,之前的版本已经支持了,各位可参见 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

 

  • 查询每个章的名字必须满足某条件的所有书籍。

我们可以在仓库的数据层,使用下面的 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
().All(c => c.Name == chapterName));
q = q.OrderBy(e => e.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 NOT (EXISTS (    SELECT 1    FROM [Chapter] AS [T1]    WHERE [T1].[BookId] = [T0].[Id] AND [T1].[Name] != @p0))ORDER BY [T0].[Name] ASC

 

  • 查询某个章中所有节必须满足某条件的所有书籍。

我们可以在仓库的数据层,使用下面的 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
().Any(s => s.Name.Contains(sectionName))));
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 EXISTS (        SELECT 1        FROM [Section] AS [T2]        WHERE [T2].[ChapterId] = [T1].[Id] AND [T2].[Name] LIKE @p0    ))ORDER BY [T0].[Name] ASC

 

  • 同时,这些查询也可以支持分页。例如,我们在上面的查询添加一个分页条件,代码如下:
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
().Any(s => s.Name.Contains(sectionName))));
q = q.OrderBy(b => b.Name);
return this.QueryList(q, new PagingInfo(2, 1));//分页
}

分成的 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

 

头晕,越来越复杂……不过经过测试,上面都没有什么问题。

下面是一个单元测试生成的分页、复杂聚合查询的 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

 

刚开始支持 Linq 查询的时候,就已经把聚合查询的单元测试给写了。鉴于比较复杂,所以一直没有实现。这周总算完成了这部分代码,心中一块石头落了地。Smile

转载地址:http://uxfql.baihongyu.com/

你可能感兴趣的文章
行业巨头联合抢占物联网与智能制造先机
查看>>
云服务器 ECS 建站教程:搭建Joomla基础管理平台
查看>>
当我们的数据被泄后,黑客在干什么?
查看>>
如何成为真正发挥大数据作用的数据驱动型公司?
查看>>
RavenDB:基于Windows/.NET平台的NoSQL数据库
查看>>
陕西师范大学:“资助大数据”助推“精准扶贫”
查看>>
浅述云空间与网盘之间的差别所在
查看>>
中国电信密集开通南沙多个光缆4G基站
查看>>
清华产业十大创新项目评选 新华三H3Cloud OS夺冠
查看>>
Synaptics车用指纹传感器
查看>>
MXNet 0.11发布,加入动态图接口Gluon,还有两位CMU教授的亲笔教程
查看>>
AIM推出医疗设备RFID系统干扰评估标准
查看>>
《系统分析与设计方法及实践》一2.2 敏捷软件开发
查看>>
5G全球统一标准有望形成 中国话语权提升
查看>>
天龙光电毛利率异常 数千万元预收不知从何而来
查看>>
双态IT时代,你需要什么样的IT咨询服务?
查看>>
iOS9.3激活失败 疑似激活服务器被挤爆
查看>>
最低调的恶意软件之Dimnie瞄准GitHub开发人员
查看>>
运营商发展大数据的四大误区
查看>>
Facebook新的图搜索?效果不是很理想
查看>>