Ответ
В Entity Framework Core есть три основных стратегии загрузки связанных данных. Выбор зависит от сценария использования.
1. Жадная загрузка (Eager Loading)
Загружает основную сущность и указанные связанные данные одним запросом. Используется, когда связанные данные нужны сразу.
var blog = context.Blogs
.Include(b => b.Posts) // Загружаем коллекцию постов
.ThenInclude(p => p.Tags) // Загружаем теги для каждого поста
.Include(b => b.Author) // Загружаем данные об авторе
.FirstOrDefault(b => b.Id == blogId);
Плюсы: Один запрос к БД, предсказуемая производительность.
Минусы: Может привести к избыточной загрузке данных (проблема SELECT N+1 наоборот) и сложным запросам с множеством JOIN.
2. Явная загрузка (Explicit Loading)
Загружает связанные данные для уже полученной сущности отдельным запросом, когда они понадобились.
var blog = context.Blogs.Find(blogId);
// Загружаем коллекцию постов отдельным запросом
context.Entry(blog)
.Collection(b => b.Posts)
.Load();
// Загружаем ссылку на автора отдельным запросом
context.Entry(blog)
.Reference(b => b.Author)
.Load();
Плюсы: Полный контроль над тем, что и когда загружается.
Минусы: Множественные запросы к БД (риск SELECT N+1).
3. Ленивая загрузка (Lazy Loading)
Связанные данные загружаются автоматически при первом обращении к навигационному свойству. Требует установки пакета Microsoft.EntityFrameworkCore.Proxies и виртуальных свойств.
// Конфигурация в DbContext
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseLazyLoadingProxies();
}
// Модель
public class Blog
{
public virtual ICollection<Post> Posts { get; set; } // Виртуальное свойство
}
// Использование
var blog = context.Blogs.Find(blogId);
var posts = blog.Posts; // Запрос к БД выполнится здесь, при первом обращении
Плюсы: Удобство разработки, не нужно думать о загрузке заранее. Минусы: Скрытые запросы к БД, сложность отладки производительности, риск циклических зависимостей.
Рекомендации и оптимизация:
- Используйте
AsNoTracking()для операций только для чтения, чтобы избежать накладных расходов на отслеживание изменений. - Применяйте фильтрацию в
Include(доступно с EF Core 5.0), чтобы не загружать все связанные данные, а только нужные.var blog = context.Blogs .Include(b => b.Posts.Where(p => p.IsPublished)) .FirstOrDefault(b => b.Id == blogId); - Рассмотрите проекции (
Select) как альтернативу загрузке полных сущностей, если нужны только конкретные поля.var blogData = context.Blogs .Where(b => b.Id == blogId) .Select(b => new { b.Title, PostTitles = b.Posts.Select(p => p.Title).ToList() }).FirstOrDefault();