ORTA
Projection & DTO Mapping
Veritabanından sadece ihtiyacın olan sütunları çeker — tüm entity'yi yüklemek yerine. Include() tüm sütunları getirir, Select() sadece belirttiklerini.
Veritabanı sağlayıcısı
Bu sayfadaki eşleşen örnekleri seçilen sağlayıcıya göre gösterir.
Neden Projection?
// ❌ Kötü: Tüm sütunları çek, sonra DTO'ya maple
var products = await context.Products
.Include(p => p.Category)
.ToListAsync();
var dtos = products.Select(p => new ProductDto { ... });
// Problem: Tüm sütunlar çekilir + tracking overhead
// ✅ İyi: Direkt projection — sadece gereken sütunlar
var dtos = await context.Products
.Select(p => new ProductDto
{
Id = p.Id,
Name = p.Name,
CategoryName = p.Category.Name
})
.ToListAsync();
-- ❌ Kötü yaklaşım: her şeyi çeker
SELECT [p].*, [c].* FROM [Products] AS [p]
INNER JOIN [Categories] AS [c] ON [p].[CategoryId] = [c].[Id];
-- ✅ İyi yaklaşım: sadece 3 sütun
SELECT [p].[Id], [p].[Name], [c].[Name] AS [CategoryName]
FROM [Products] AS [p]
INNER JOIN [Categories] AS [c] ON [p].[CategoryId] = [c].[Id];
-- ❌ Kötü yaklaşım: her şeyi çeker
SELECT p.*, c.* FROM products AS p
INNER JOIN categories AS c ON p.category_id = c.id;
-- ✅ İyi yaklaşım: sadece 3 sütun
SELECT p.id, p.name, c.name AS category_name
FROM products AS p
INNER JOIN categories AS c ON p.category_id = c.id;
Nested (İç İçe) Projection
var orders = await context.Orders
.Select(o => new OrderDetailDto
{
OrderId = o.Id,
CustomerName = o.Customer.Name,
TotalAmount = o.LineItems.Sum(li => li.Quantity * li.UnitPrice),
Items = o.LineItems.Select(li => new OrderItemDto
{
ProductName = li.Product.Name,
Quantity = li.Quantity,
LineTotal = li.Quantity * li.UnitPrice
}).ToList(),
ItemCount = o.LineItems.Count()
})
.ToListAsync();
SELECT [o].[Id] AS [OrderId],
[c].[Name] AS [CustomerName],
(SELECT SUM([li].[Quantity] * [li].[UnitPrice]) FROM [OrderLineItems] AS [li]
WHERE [li].[OrderId] = [o].[Id]) AS [TotalAmount],
(SELECT COUNT(*) FROM [OrderLineItems] AS [li0]
WHERE [li0].[OrderId] = [o].[Id]) AS [ItemCount],
[li1].[ProductName], [li1].[Quantity], [li1].[Quantity] * [li1].[UnitPrice] AS [LineTotal]
FROM [Orders] AS [o]
INNER JOIN [Customers] AS [c] ON [o].[CustomerId] = [c].[Id]
LEFT JOIN (
SELECT [li].[OrderId], [p].[Name] AS [ProductName], [li].[Quantity], [li].[UnitPrice]
FROM [OrderLineItems] AS [li]
INNER JOIN [Products] AS [p] ON [li].[ProductId] = [p].[Id]
) AS [li1] ON [li1].[OrderId] = [o].[Id]
ORDER BY [o].[Id];
SELECT o.id AS order_id,
c.name AS customer_name,
(SELECT SUM(li.quantity * li.unit_price) FROM order_line_items AS li
WHERE li.order_id = o.id) AS total_amount,
(SELECT COUNT(*) FROM order_line_items AS li0
WHERE li0.order_id = o.id) AS item_count,
li1.product_name, li1.quantity, li1.quantity * li1.unit_price AS line_total
FROM orders AS o
INNER JOIN customers AS c ON o.customer_id = c.id
LEFT JOIN (
SELECT li.order_id, p.name AS product_name, li.quantity, li.unit_price
FROM order_line_items AS li
INNER JOIN products AS p ON li.product_id = p.id
) AS li1 ON li1.order_id = o.id
ORDER BY o.id;
Koşullu Projection
// Farklı roller için farklı DTO
var products = await context.Products
.Select(p => isAdmin
? new ProductDto { Id = p.Id, Name = p.Name, Cost = p.Cost, Margin = p.Price - p.Cost }
: new ProductDto { Id = p.Id, Name = p.Name, Cost = 0, Margin = 0 })
.ToListAsync();
// Null-safe navigation (EF otomatik LEFT JOIN yapar)
var dtos = await context.Products
.Select(p => new
{
p.Name,
CategoryName = p.Category != null ? p.Category.Name : "Kategorisiz"
})
.ToListAsync();
SELECT [p].[Name], COALESCE([c].[Name], N'Kategorisiz') AS [CategoryName]
FROM [Products] AS [p]
LEFT JOIN [Categories] AS [c] ON [p].[CategoryId] = [c].[Id];
SELECT p.name, COALESCE(c.name, 'Kategorisiz') AS category_name
FROM products AS p
LEFT JOIN categories AS c ON p.category_id = c.id;
Projection vs Include — Karar Tablosu
| Senaryo | Tercih | Neden |
|---|---|---|
| API'ye DTO dönüyorsun | Projection | Sadece gereken sütunlar |
| Entity üzerinde güncelleme yapacaksın | Include | Change tracking gerekli |
| Rapor / Dashboard | Projection | Aggregate + minimal veri |
| Admin panel (CRUD) | Include | Entity'yi komple ihtiyacın var |
| Performans kritik (liste sorguları) | Projection | Network + bellek tasarrufu |