Ответ
В Entity Framework Core соединения выполняются в основном через LINQ-запросы. Вот основные подходы с пояснениями, когда что использовать:
1. Неявное соединение через навигационные свойства (Самый частый и удобный способ): EF автоматически генерирует JOIN при обращении к связанным данным.
// EF сам построит JOIN между Products и Categories
var products = await _context.Products
.Where(p => p.Category.Name == "Electronics")
.ToListAsync();
2. Явное соединение с Join (Полезно при отсутствии навигационных свойств или для сложных ключей):
var query = from product in _context.Products
join category in _context.Categories
on product.CategoryId equals category.Id
select new { product.Name, CategoryName = category.Name };
3. Жадная загрузка связанных данных с Include и ThenInclude (Для загрузки связанных сущностей в одном запросе):
// Загружаем продукты вместе с их категориями и поставщиками
var products = await _context.Products
.Include(p => p.Category) // JOIN с Categories
.ThenInclude(c => c.Supplier) // JOIN с Suppliers (через Category)
.ToListAsync();
4. Левое внешнее соединение (Left Join) с GroupJoin и SelectMany + DefaultIfEmpty:
var query = from category in _context.Categories
join product in _context.Products
on category.Id equals product.CategoryId into productsGroup
from product in productsGroup.DefaultIfEmpty() // Ключевой момент для LEFT JOIN
select new { category.Name, ProductName = product.Name ?? "No products" };
Важные замечания:
- Производительность: Всегда проверяйте сгенерированный SQL через
ToQueryString()или профилировщик. ИногдаIncludeможет привести к избыточным данным (проблема N+1 или cartesian explosion) — в таких случаях используйтеSelectдля проекции только нужных полей. - Метод синтаксиса: Аналогичные операции можно выполнять и в цепочке методов (Method Syntax).