Получение пагинации для работы с одним ко многим присоединиться - PullRequest
1 голос
/ 28 июня 2019

В настоящее время я работаю над базой данных с несколькими отношениями «один ко многим» и «многие ко многим», и я изо всех сил стараюсь, чтобы ормлит работал хорошо.

У меня есть отношение один ко многимвот так:

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

Мне нужно вернуть коллекцию 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

В документации нет примеров этого, и я изо всех сил пытался это выяснить.Я не вижу ничего, что могло бы работать как array_agg в Sql объекте OrmLite.

Я пробовал варианты:

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 в список и сохранить правильность подкачки и смещения.

Возможно ли это?Любой обходной путь?

edit:

Я сделал проект, который вы можете запустить, чтобы увидеть проблему:

https://github.com/GuerrillaCoder/OneToManyIssue

База данных добавлена ​​как докер, который вы можете запуститьdocker-compose up.Надеюсь, это показывает, что я пытаюсь сделать

1 Ответ

1 голос
/ 29 июня 2019

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);

Это вернет словарь динамического результирующего набора , который вам нужно будет объединить в отдельную коллекцию идентификаторов для запроса всех электронных книг, на которые есть ссылки, например:

//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 и использовать его для заполнения коллекции сущностей электронных книг, используя идентификаторы для каждой строки:

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>());
...