zoukankan      html  css  js  c++  java
  • 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 查询的时候,就已经把聚合查询的单元测试给写了。鉴于比较复杂,所以一直没有实现。这周总算完成了这部分代码,心中一块石头落了地。Smile

  • 相关阅读:
    C语言运算符优先级和口诀
    跨域问题的解决方案 php
    浅谈跨域攻击及预防
    浅析Websocket--PHP
    linux下的删除目录和文件的方法
    python魔法方法
    双指针
    python常用模块
    python三大器
    对闭包的误区
  • 原文地址:https://www.cnblogs.com/zgynhqf/p/4270330.html
Copyright © 2011-2022 走看看