通过分页进行一对多连接

时间:2019-06-28 03:22:37

标签: servicestack

我目前正在处理具有几个一对多和多对多关系的数据库,而我正在努力让ormlite正常工作。

我有一对多的关系,

var q2 = Db.From<GardnerRecord>()
    .LeftJoin<GardnerRecord, GardnerEBookRecord>((x, y) => x.EanNumber == y.PhysicalEditionEan)

我需要返回具有ProductDto嵌套列表的GardnerEBookRecord集合。

使用SelectMult()技术不起作用,因为在我将左连接的结果压缩为较小的集合时分页会中断,因此页面大小和偏移量都是错误的(此方法:How to return nested objects of many-to-many relationship with autoquery

要正确进行分页,我需要能够执行以下操作:

select r.*, count(e) as ebook_count, array_agg(e.*)
from gardner_record r
       left join gardner_e_book_record e
                 on r.ean_number = e.physical_edition_ean
group by r.id

文档中没有此示例,我一直在努力找出答案。我在OrmLite的array_agg对象中看不到任何像Sql一样起作用的东西。

我尝试了以下形式的变化:

var q2 = Db.From<GardnerRecord>()
    .LeftJoin<GardnerRecord, GardnerEBookRecord>((x, y) => x.EanNumber == y.PhysicalEditionEan)
    .GroupBy(x => x.Id).Limit(100)
    .Select<GardnerRecord, GardnerEBookRecord>((x, y) => new { x, EbookCount = Sql.Count(y), y }) //how to aggregate y?

var res2 = Db.SelectMulti<GardnerRecord, GardnerEBookRecord>(q2);

var q2 = Db.From<GardnerRecord>()
    .LeftJoin<GardnerRecord, GardnerEBookRecord>((x, y) => x.EanNumber == y.PhysicalEditionEan)
    .GroupBy(x => x.Id).Limit(100)
    .Select<GardnerRecord, List<GardnerEBookRecord>>((x, y) => new { x, y });

var res = Db.SqlList<object>(q2);

但是我不知道如何将GardnerEBookRecord聚合到一个列表中并保持分页和偏移正确。

这可能吗?有解决方法吗?

编辑:

我创建了一个项目,您可以运行该项目以查看问题:

https://github.com/GuerrillaCoder/OneToManyIssue

作为docker添加的数据库可以运行docker-compose up。希望这可以显示我正在尝试做的事情

1 个答案:

答案 0 :(得分:1)

Npgsql不支持读取未知数组或记录列类型,例如array_agg(e.*),失败,显示:

  

未处理的异常:System.NotSupportedException:字段'ebooks'具有Npgsql当前未知的类型(OID 347129)。

但是它确实支持使用array_agg(e.id)读取整数数组,您可以改为查询:

var q = @"select b.*, array_agg(e.id) ids from book b
         left join e_book e on e.physical_book_ean = b.ean_number
         group by b.id";

var results = db.SqlList<Dictionary<string,object>>(q);

这将返回一个Dictionary Dynamic Result Set,您需要将其合并成一个不同的ID集合以查询所有引用的电子书,例如:

//Select All referenced EBooks in a single query  
var allIds = new HashSet<int>();
results.Each(x => (x["ids"] as int[])?.Each(id => allIds.Add(id)));
var ebooks = db.SelectByIds<EBook>(allIds);

然后,您可以创建id => Ebook的字典映射,并使用该映射使用每行的ID填充电子书实体的集合:

var ebooksMap = ebooks.ToDictionary(x => x.Id);
results.Each(x => x[nameof(ProductDto.Ebooks)] = (x["ids"] as int[])?
    .Where(id => id != 0).Map(id => ebooksMap[id]) );

然后您可以使用ServiceStack AutoMapping Utils将每个对象字典转换为产品DTO:

var dtos = results.Map(x => x.ConvertTo<ProductDto>());