EFEF Core Handbook

İLERİ

Complex Types — EF Core 8+

Owned Entity'nin modern alternatifi. Kimliği (Id) olmayan, birden fazla entity'de tekrar kullanabileceğin değer nesneleri tanımlar. Para birimi, koordinat, renk gibi yapılar için.

Veritabanı sağlayıcısı Bu sayfadaki eşleşen örnekleri seçilen sağlayıcıya göre gösterir.

Gerçek Senaryo: Ürün fiyatı — para birimi ile birlikte

decimal Price yazmak yetmez — hangi para birimi? Money tipini hem Product hem Order hem başka entityler kullanabilir.

// --- Tipler ---

public record Money(decimal Amount, string Currency);   // record → immutable

public class Product
{
    public int    Id        { get; set; }
    public string Name      { get; set; }
    public Money  Price     { get; set; }   // satış fiyatı
    public Money  CostPrice { get; set; }   // maliyet fiyatı
}

public class Order
{
    public int   Id    { get; set; }
    public Money Total { get; set; }   // aynı Money tipi burada da kullanılıyor
}

// --- Config ---

public class ProductConfiguration : IEntityTypeConfiguration<Product>
{
    public void Configure(EntityTypeBuilder<Product> builder)
    {
        builder.ToTable("Products");
        builder.HasKey(p => p.Id);
        builder.Property(p => p.Name).IsRequired().HasMaxLength(200);

        builder.ComplexProperty(p => p.Price, price =>
        {
            price.Property(m => m.Amount)  .HasColumnName("Price_Amount")  .HasPrecision(18, 2);
            price.Property(m => m.Currency).HasColumnName("Price_Currency").HasMaxLength(3);
        });

        builder.ComplexProperty(p => p.CostPrice, cost =>
        {
            cost.Property(m => m.Amount)  .HasColumnName("Cost_Amount")  .HasPrecision(18, 2);
            cost.Property(m => m.Currency).HasColumnName("Cost_Currency").HasMaxLength(3);
        });
    }
}

Oluşan SQL:

CREATE TABLE [Products] (
    [Id]             INT           IDENTITY(1,1) NOT NULL,
    [Name]           NVARCHAR(200) NOT NULL,
    [Price_Amount]   DECIMAL(18,2) NOT NULL,
    [Price_Currency] NVARCHAR(3)   NOT NULL,
    [Cost_Amount]    DECIMAL(18,2) NOT NULL,
    [Cost_Currency]  NVARCHAR(3)   NOT NULL,
    CONSTRAINT [PK_Products] PRIMARY KEY CLUSTERED ([Id])
);
CREATE TABLE products (
    id             INT           GENERATED BY DEFAULT AS IDENTITY,
    name           VARCHAR(200)  NOT NULL,
    price_amount   NUMERIC(18,2) NOT NULL,
    price_currency VARCHAR(3)    NOT NULL,
    cost_amount    NUMERIC(18,2) NOT NULL,
    cost_currency  VARCHAR(3)    NOT NULL,
    CONSTRAINT pk_products PRIMARY KEY (id)
);

Örnek veri:

Id Name Price_Amount Price_Currency Cost_Amount Cost_Currency
1 Laptop 84999.99 TRY 62000.00 TRY
2 iPhone 54999.00 TRY 41000.00 TRY
3 AirPods 299.00 USD 180.00 USD

Kullanımda:

var product = new Product
{
    Name      = "Laptop",
    Price     = new Money(25000, "TRY"),
    CostPrice = new Money(18000, "TRY")
};

// LINQ sorgusu — EF SQL'e çevirir
var pahalılar = context.Products
    .Where(p => p.Price.Amount > 10000 && p.Price.Currency == "TRY")
    .ToList();
-- EF'in ürettiği SQL:
SELECT [p].[Id], [p].[Name], [p].[Price_Amount], [p].[Price_Currency], 
       [p].[Cost_Amount], [p].[Cost_Currency]
FROM [Products] AS [p]
WHERE [p].[Price_Amount] > 10000.0 AND [p].[Price_Currency] = N'TRY';
-- EF'in ürettiği SQL:
SELECT p.id, p.name, p.price_amount, p.price_currency, 
       p.cost_amount, p.cost_currency
FROM products AS p
WHERE p.price_amount > 10000.0 AND p.price_currency = 'TRY';

Owned Entity ile Complex Type farkı

Owned Entity Complex Type (EF8+)
Ayrı tabloya taşınabilir mi? ToTable(...)
JSON'a map edilebilir mi? ToJson() EF Core 10+ 🆕
Koleksiyon olabilir mi? OwnsMany (JSON içinde EF10)
Struct kullanılabilir mi? EF Core 10+ 🆕
Optional olabilir mi? EF Core 10+ 🆕
ExecuteUpdate desteği EF Core 10+ 🆕
Semantik Kimliksiz entity gibi davranır Gerçek value object
Ne zaman tercih et Legacy, koleksiyon / ayrı tablo gerekiyorsa Yeni projelerde her zaman tercih et (EF10 ile tam destek)

🆕 EF Core 10 ile Complex Types artık tavsiye edilen yaklaşım:

  • JSON mapping (b.ComplexProperty(c => c.Address, c => c.ToJson()))
  • Struct desteği (identity olmayan value object'ler için doğal)
  • Optional complex types (Address?)
  • ExecuteUpdateAsync ile bulk güncelleme
  • Owned entity'lerdeki "aynı referans atama" sorunu yok (value semantics)