Пагинация с использованием ISpecification

171
14 июля 2018, 17:40

Есть сущность

public class Message : BaseEntity
{
    // some other fields
    public int[] AttachmentsIds { get; set; }
    public Attachment[] Attachments { get; set; } 
}

Планировалось, что первое поле будет браться из БД, потом по списку Id будет запрошены данные из другой таблицы и заполнено второе поле.

Для этого я думал написать репозитории для Message и Attachments, а затем сервис, который используя оба эти репозитория бы возвращал уже заполненную сущность Message или коллекцию сущностей.

Примерно так я представлял себе метод для получения всех сообщений с заполненными вложениями

public interface ISpecification<T>
{
    Expression<Func<T, bool>> Criteria { get; }
}
//...
IMessageRepository messageRep = ... ;
IAttachmentRepository attachmentRep = ... ;
//...
public IEnumerable<Message> GetMessagesWithAttachments(ISpecification<Message> spec)
{
    IEnumerable<Message> messages = messageRep.All.Where(spec.Criteria).ToList(); //All - IQueryble
    IEnumerable<int> attachmentsIds = messages.Select(m => m.AttachmentsIds).SelectMany(ids => ids).Distinct().ToList();
    IEnumerable<Attachments> attachments = attachmentRep.All.Where(id => attachmentsIds.Contains(id));
    foreach (var message in messages)
    {
        message.Attachments = attachmentsIds.Where(id => message.AttachmentsIds.Contains(id)).ToList();
    }
    return messages;
}

Запрашиваем все посты по фильтру, получаем список встреченных вложений, получаем его, а затем тасуем вложения по сообщениям.

Но если я хочу использовать пагинацию (или просто Skip и Take) или если мне необходимо сначала отсортировать, а потом сделать выборку, то непонятно как это сделать.

Решение в лоб, которое приходит в голову:

// модифицировать метод
public IEnumerable<Message> GetMessagesWithAttachments(IQueryble<Message> messages)
{
    IEnumerable<int> attachmentsIds = messages.Select(m => m.AttachmentsIds).SelectMany(ids => ids).Distinct().ToList();
    IEnumerable<Attachments> attachments = attachmentRep.All.Where(id => attachmentsIds.Contains(id));
    foreach (var message in messages)
    {
        message.Attachments = attachmentsIds.Where(id => message.AttachmentsIds.Contains(id)).ToList();
    }
    return messages;
}
//...
//и вызывать так:
IEnumerable<Message> messages = messageService.GetMessagesWithAttachments(
    messageService.All
        .OrderBy(...)
        .Where(...)
        .Skip(...)
        .Take(...)
);

Но решение не выглядит красивым и/или правильным =/

(Если есть советы по поводу самой архитектуры, то вот тут вопрос, на который ещё не ответили: Строки одной таблицы как столбец в другой )

READ ALSO
Как настроить коллбеки для Яндекс Кассы в php-окружении?

Как настроить коллбеки для Яндекс Кассы в php-окружении?

Есть ли какой-либо мануал более вменяемый чем официальная документация?

186
Проблема с !empty

Проблема с !empty

У меня есть данный код:

196