让分页与一对多连接一起工作
Getting pagination to work with one to many join
我目前正在处理一个具有多个一对多和多对多关系的数据库,我正在努力让 ormlite 很好地工作。
我有这样的一对多关系:
var q2 = Db.From<GardnerRecord>()
.LeftJoin<GardnerRecord, GardnerEBookRecord>((x, y) => x.EanNumber == y.PhysicalEditionEan)
我需要 return ProductDto
的集合,其中包含 GardnerEBookRecord
.
的嵌套列表
使用 SelectMult()
技术它不起作用,因为分页中断,因为我将左连接结果压缩到较小的集合,因此页面大小和偏移量都是错误的(此方法:)
为了获得正确的分页,我需要能够执行类似的操作:
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 的 Sql
对象中看不到任何功能类似于 array_agg
的东西。
我试过以下变体:
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
。希望这显示了我正在尝试做的事情
Npgsql 不支持读取未知数组或记录列类型,例如 array_agg(e.*)
失败:
Unhandled Exception: System.NotSupportedException: The field 'ebooks' has a type currently unknown to 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);
这将 return 一个 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>());
我目前正在处理一个具有多个一对多和多对多关系的数据库,我正在努力让 ormlite 很好地工作。
我有这样的一对多关系:
var q2 = Db.From<GardnerRecord>()
.LeftJoin<GardnerRecord, GardnerEBookRecord>((x, y) => x.EanNumber == y.PhysicalEditionEan)
我需要 return ProductDto
的集合,其中包含 GardnerEBookRecord
.
使用 SelectMult()
技术它不起作用,因为分页中断,因为我将左连接结果压缩到较小的集合,因此页面大小和偏移量都是错误的(此方法:
为了获得正确的分页,我需要能够执行类似的操作:
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 的 Sql
对象中看不到任何功能类似于 array_agg
的东西。
我试过以下变体:
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
。希望这显示了我正在尝试做的事情
Npgsql 不支持读取未知数组或记录列类型,例如 array_agg(e.*)
失败:
Unhandled Exception: System.NotSupportedException: The field 'ebooks' has a type currently unknown to 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);
这将 return 一个 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>());