EF, репозитории и пересечение совокупных границ

У меня есть два сводных корня в моем домене и, следовательно, два репозитория. Мы назовем их BookRepository и AuthorRepository для примера.

Я разрабатываю приложение MVC, и на одной странице должна отображаться таблица, содержащая список авторов, где каждая строка показывает личные данные автора. В конце каждой строки есть небольшая кнопка, которую можно щелкнуть, чтобы развернуть строку и отобразить дочернюю таблицу с подробным описанием книг, опубликованных автором.

Когда страница загружается, выполняется некоторый ajax для получения сведений об авторе из контроллера API и отображения данных в таблице. Каждое свойство в объекте Author почти напрямую сопоставляется со столбцом, за одним исключением, и здесь у меня возникла проблема. Я хочу, чтобы кнопка в конце каждой строки была отключена, если и только если у автора нет опубликованных книг. Это означает, что с каждой записью об авторе должно возвращаться логическое значение, указывающее, есть ли у него опубликованные книги.

В моем репозитории книг есть несколько таких методов:

public IEnumerable<Book> GetBooksForAuthor(int authorId);

public bool AnyBooksForAuthor(int authorId);

и мой класс Book имеет свойство AuthorId, поэтому я могу получить автора книги, вызвав

authorRepository.GetById(book.AuthorId);

Моя проблема в том, что для создания строки для моей вышеупомянутой таблицы мне нужно создать ее следующим образом:

IEnumerable<Author> authors = authorRepository.GetAll();
foreach (Author author in authors)
{
    yield return new AuthorTableRow
    {
        Name = author.Name,
        Age = author.Age,
        Location = author.PlaceOfResidence.Name,
        HasBooks = this.bookRepository.AnyBooksForAuthor(author.Id)
    };
}

Вышеприведенный код кажется правильным, но при вызове this.bookRepository.AnyBooksForAuthor(author.Id) для каждого отдельного автора возникает значительное снижение производительности, поскольку каждый раз выполняется вызов базы данных.

В идеале, я полагаю, мне нужен AuthorTableRowRepository, который мог бы выполнять что-то вроде следующего:

public IEnumerable<AuthorTableRow> GetAll()
{
    return from a in this.dbContext.Authors
           select new AuthorTableRow
           {
               Name = a.Name,
               Age = a.Age,
               Location a.PlaceOfResidence.Name
               HasBooks = a.Books.Any()
           });
}

Я не решаюсь поставить это на место по следующим причинам:

  • AuthorTableRowRepository — это репозиторий AuthorTableRows, но строка AuthorTable не является ни объектом домена, ни корнем агрегата, и поэтому не должна иметь собственного репозитория.
  • Поскольку Author и Book являются совокупными корнями, я удалил свойство «Книги» из сущности Author, потому что хотел, чтобы единственный способ получить книги — через BookRepository. Это делает HasBooks = a.Books.Any() невозможным. Я не уверен, навязываю ли я здесь свою ошибочную передовую практику. Кажется неправильным получать книги, получая Author через AuthorRepository, а затем просматривая его свойство Books, и наоборот, получая Author через свойство объекта Book. Полагаю, я бы назвал это пересечением совокупных корневых границ?

Как бы другие люди решили это? Мои опасения беспочвенны? Меня больше всего беспокоит (какой должна быть) производительность в первом методе, но я хочу придерживаться передовой практики с шаблоном репозитория и DDD.


person dark_perfect    schedule 19.09.2014    source источник
comment
Почему минусы...? Пожалуйста, объясните, чтобы я мог избежать ошибок в следующий раз?   -  person dark_perfect    schedule 01.10.2014


Ответы (2)


Я бы придерживался первого подхода, но попытался бы оптимизировать вещи в методе bookrepository. Например, вы можете загрузить всю эту информацию за один раз и использовать поиск в памяти, чтобы ускорить это. Таким образом, вам потребуется 2 запроса, а не 1 для каждого автора.

person Tom Van Acker    schedule 22.09.2014
comment
Как можно оптимизировать метод bookrepository? Это довольно простой запрос .Any() к базе данных, я не уверен, что еще я могу сделать, чтобы оптимизировать его? - person dark_perfect; 23.09.2014
comment
Что ж, если бы в книжном репозитории был список в памяти для количества книг по авторам, вам нужно было бы только инициализировать список перед циклом, как здесь; this.bookRepository.LoadBooksByAuthor(); IEnumerable<Author> authors = authorRepository.GetAll(); foreach (Author author in authors) { yield return new AuthorTableRow { Name = author.Name, Age = author.Age, Location = author.PlaceOfResidence.Name, HasBooks = this.bookRepository.AnyBooksForAuthor(author.Id) // this call does not go to the database, but is just an in-memory lookup }; } - person Tom Van Acker; 24.09.2014
comment
Действительно ли это лучше, если в bookRepository есть миллионы книг для загрузки? bookRepository также не может загрузить подмножество таблицы, потому что он не знает, о каких книгах вам нужна информация, пока не будет выполнен запрос автора? - person dark_perfect; 24.09.2014
comment
Действительно, я бы не загружал все книги... только общее количество книг по авторам. Этот список памяти будет храниться в записи для каждого автора... Если это неприемлемо, вам следует подумать о том, чтобы быть прагматичным и позволить AuthorRepository возвращать количество книг вместе с каждой записью автора. - person Tom Van Acker; 25.09.2014

В конце концов я решил это, создав Entity из представления в базе данных. Я назвал объект «AuthorSummary» и создал AuthorSummaryRepository, который не содержал никаких методов Add(), а только методы поиска.

person dark_perfect    schedule 20.10.2014