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 分页
}
 

支持的一般查询



使用 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<Chapter>().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<Section>().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<Section>().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 查询的时候,就已经把聚合查询的单元测试给写了。鉴于比较复杂,所以一直没有实现。这周总算完成了这部分代码,心中一块石头落了地。

时间: 2024-12-03 19:32:55

Rafy 中的 Linq 查询支持(根据聚合子条件查询聚合父)的相关文章

c#中怎样用js写ASPxGridView的全局条件查询

问题描述 c#中怎样用js写ASPxGridView的全局条件查询,就是在一个文本框中输入任意条件,即可查询ASPxGridView中的数据的写法,求各位大神帮帮忙,小女子万分感激 解决方案 解决方案二:在数据库中叫全文索引,在页面你ctrl+f亲.这种一般就要很多个or了.如果你做了全民检索的话.就直接用全文检索就行了解决方案三:用jQuery调用WebServices也可以实现.可以参考这个思路:一.引入脚本如下<scripttype="text/javascript"src

c#多条件查询,怎么写语句请教各位c#多条件查询请教各位c#多条件查询

问题描述 c#多条件查询,怎么写语句请教各位c#多条件查询请教各位c#多条件查询 请教各位c#多条件查询,请教各位c#多条件查询请教各位c#多条件查询 解决方案 C#用到的多条件查询 解决方案二: 多条件无非就是拼接下sql,用and连接各个条件.

linq to entities-Linq to entities 多条件查询问题不知道有什么方法呢。

问题描述 Linq to entities 多条件查询问题不知道有什么方法呢. 在用实体类建模时候,用Linq根据条件查询数据表,对于多个条件的查询,原来是需要什么条件就写一个方法,在开发途中发现条件确实是太多= =, 便想用几个公用方法来包含这些条件,也就是条件为空的时候不执行这个where罢了 比如: if (FromUserId != """" && ToUserId != """") { var que

db2 查询 配置参数-关于db2条件查询中的to_date问题

问题描述 关于db2条件查询中的to_date问题 select a from t where b = '0' and to_date(c,'yyyy-mm-dd HH24:MI:SS') >= to_date('2016-01-05 00:00:00','yyyy-mm-dd HH24:MI:SS') and d = '9' order by e desc 执行出错,我上网查了下,改成了 select a from t where b = '0' and values to_date(c,'y

不同条件查询-mysql 根据不同的条件查询出想要的结果,以List或Bean等形式返回。在线等,谢谢啦!!

问题描述 mysql 根据不同的条件查询出想要的结果,以List或Bean等形式返回.在线等,谢谢啦!! 有一张mySql表:tb_products,表结构如下图: 如何根据条件查询出结果集!条件如下图: 上图中查询条件可能有的为空!! Jvava 后台代码:用这种工具类:QueryRunner qr = new QueryRunner(DataSourceUtils.getDataSource()); 如何继续写呢??以List或Bean等形式返回.

ibatis多条件查询-ibatis开始实现多条件查询么???

问题描述 ibatis开始实现多条件查询么??? 我用的struts2+spring+ibatis框架,对于单表操作,ibatis是很方便的,可是对于多表呢??我要做查询,但是查询有几条sql语句,查询一条,另外还有几条sql是前提条件,满足前提条件的数据再来做查询...求大神指导.

怎么根据查询出的信息为条件查询同条件信息

问题描述 表名字:abc字段名:idchengshiname字段数据:id:1,chengshi:成都市,name:张三字段数据:id:2,chengshi:成都市,name:李四字段数据:id:3,chengshi:成都市,name:赵二字段数据:id:4,chengshi:北京市,name:王当字段数据:id:5,chengshi:北京市,name:邱雨字段数据:id:6,chengshi:天津市,name:何东字段数据:id:7,chengshi:天津市,name:张四字段数据:id:8,

asp.net-AspNetPager分页控件在同一页面中按条件查询的数据分页的连续问题

问题描述 AspNetPager分页控件在同一页面中按条件查询的数据分页的连续问题 AspNetPager分页控件在同一个页面中多次修改SQL查询语句后的问题,如何在PageChanged事件中取得最新一次查询的SQL字符串值. 我需要在一个页面中根据搜索条件,多次修改SQL查询语句,可是分页控件的BindData(string sql)方法, 在PageChanged事件中引用的private string sql变量却总是首次加载页面时的值,点击分页后就变回了 原来的查询结果.如何使用Asp

sql多条件查询,如何高效组合多个条件

问题描述 sql多条件查询,如何高效组合多个条件 我想查询数据库,是条件查询,但是这个条件有可能比较多,比如有几百个条件,如何写一条sql语句能高效查询数据库中满足这些条件的数据呢? 例如,我想查询userid字段为a,b,c,d--等人的数据,这样写sql感觉效率很低 select * from TABLE where USERID=a or USERID= b or USERID=c-- 有什么方法能比较好的满足查询要求呢? 解决方案 用in不能实现吗?比如 select * from TA