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 не е обект на домейн, нито обобщен корен и следователно не трябва да има свое собствено хранилище.
  • Тъй като и авторът, и книгата са сборни корени, премахнах свойството „Книги“ от обекта автор, защото исках единственият начин за извличане на книги да бъде чрез BookRepository. Това прави HasBooks = a.Books.Any() невъзможно. Не съм сигурен обаче дали налагам собствената си заблудена най-добра практика тук. Изглежда погрешно да се получават книги чрез получаване на автор чрез AuthorRepository и след това преминаване през неговото свойство Books, и обратното при получаване на автор чрез свойство на обект 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