#c# #entity-framework #entity-framework-core #repository-pattern #unit-of-work
#c# #entity-framework #сущность-фреймворк-ядро #репозиторий-шаблон #единица работы
Вопрос:
Я создаю n-уровневое приложение, используя шаблон репозитория / единицы работы, но у меня возникают проблемы с чтением свойств связанных объектов, поскольку они не загружаются. Я определил проблему, вызванную отложенной загрузкой. Упрощенным примером может быть попытка повторного поиска автора книги в приведенном ниже коде, поскольку оба свойства Author и Publisher для Book равны нулю.
В общем, я заинтригован отложенной загрузкой, поскольку это (насколько я понимаю) должно уменьшить объем передаваемых данных. Это особенно интересно для меня, поскольку я не всегда использую все свойства или коллекции на своих страницах Razor. Тем не менее, я не уверен, что это хорошая стратегия, и рассматриваю возможность изменения всей архитектуры от шаблона репозитория / единицы работы. Я еще не решил.
public Author
{
public int Id {get; set;}
public string Name {get; set;}
public ICollection<Book> Books {get; set;}
public ICollection<Publisher> Publishers {get; set;}
}
public Book
{
public int Id {get; set;}
public string Title {get; set;}
public int AuthorId {get; set;}
public ICollection<Author> Authors {get; set;}
public Publisher Publisher {get; set;}
}
public Publisher
{
public int Id {get; set;}
public string Name {get; set;}
public ICollection<Book> Books {get; set;}
public ICollection<Author> Authors {get; set;}
}
Вернемся к проблеме. Сначала я попытался применить .Include(p=>p.Author)
or .Load(p=>p.Author)
, но понял, что это невозможно, после .ToListAsync()
чего я вызываю метод `GetAllAsync ()’ в общем репозитории.
Теперь я рассматриваю возможность внедрения DbContext
либо непосредственно на мои страницы Razor, либо на уровне сервиса, а затем цепочки Include()
вызовов, например.
_context.DBSetBooks<Book>.Include(p => p.Publisher).Where(p => p.Publisher.Name == "Manning").Include(p => p.Author);
Идея, изложенная выше, состоит в том, чтобы уменьшить объем передаваемых данных, или я неправильно понял это полностью?
Я не слишком люблю вводить DBContext
на каждой странице Razor — вот почему я в первую очередь выбираю шаблон проектирования репозитория / единицы работы.. Если я все еще хочу отложенную загрузку, должен ли я вместо этого попробовать DDD-шаблон?
Есть идеи, как действовать дальше?
Комментарии:
1.
Include
используется только для быстрой загрузки, а не для создания объединений. Они генерируются отношениями между объектами самим ORM. Что касается репозитория и UoW, они вам не нужны. DbSet уже является репозиторием, DbContext уже является единицей работы. Если вы попытаетесь переопределить шаблоны, вы только создадите ошибки и нарушите функциональность UoW2. Что касается отложенной загрузки, довольно часто это увеличивает загружаемые данные и накладные расходы из-за проблемы N 1. Вместо выполнения 1 запроса, который возвращает все необходимые данные, у вас есть 1 запрос, который возвращает только корневые объекты и N запросов для загрузки каждого объекта по мере доступа к нему. Это плохо
3. Помимо комментариев, которые вы действительно должны понять от @PanagiotisKanavos, эта часть вопроса: «Мне особенно интересно, поскольку я не всегда использую все свойства или коллекции на своих страницах Razor», говорит мне, что вы не должны использовать отложенную загрузку
Include
, а скорее создавать ViewModels с необходимыми данными иэтоSelect
вместо4. LINQ позволяет загружать только те свойства, которые вы хотите, не более того. Нет причин загружать целые объекты. Нет причин использовать шаблоны, которые были введены в 2000 году, для загрузки целых объектов в J2EE. Подумайте о DbContext как о службе данных для ограниченного контекста в DDD. Нет причин иметь один DbContext для всего приложения, отображающий всю базу данных. Имеет смысл создать специализированный репозиторий, чтобы облегчить доступ к специализированному DbContext. Однако общий контекст бесполезен. Набор баз данных уже обеспечивает это
Ответ №1:
Чтобы уменьшить объем передаваемых данных, вы должны уменьшить .include , поэтому делайте свои запросы такими (это просто идея):
Сначала создайте класс, содержащий только те поля, которые вам нужны:
public class BookInfo
{
public int Id {get; set;}
public string AuthorName {get; set;}
public string PublisherName {get; set;}
.... and so on
}
Получить список книг, используя запрос, подобный этому:
var books = await _context.Set<Book>()
.Where(p => p.Publisher.Name == "Manning")
.Select (b => new BookInfo {
Id = b.Id,
AuthorName=b.Author.Name,
PublisherName=b.Publisher.Name,
....and so on
}).ToArrayAsync();
Другая возможность заключается в том, что вы помещаете книгу частичных классов в отдельный файл с дополнительными полями, подобными этому
public partial class Book
{
[NotMapped]
public string AuthorName {get; set;}
[NotMapped]
public string PublisherName {get; set;}
}
В этом случае вы можете использовать тот же класс Book вместо BookInfo:
.Select ( b => new Book {
Id = b.Id,
AuthorName=b.Author.Name,
PublisherName=b.Publisher.Name,
....and so on
}).ToArrayAsync();
}
Комментарии:
1.
.Set<>()
может использоваться только в том случае, если объект уже настроен. В таком случае, почему бы не использоватьBooks
свойство напрямую? Ничего не получается при использованииSet<>()
2. Да, вы правы. Я не видел ваш полный dbcontext, поэтому я использую Set . Но обычно я бы использовал книги.