İLERİ
Owned Entities (Sahipli Varlıklar)
Bağımsız kimliği olmayan, her zaman sahibiyle birlikte var olan alt nesneleri aynı tabloda saklar. Adres, koordinat, para birimi gibi yapılar için — ayrı tablo + FK gerektirmez.
Veritabanı sağlayıcısı
Bu sayfadaki eşleşen örnekleri seçilen sağlayıcıya göre gösterir.
Gerçek Senaryo: E-ticaret müşterisinin fatura ve teslimat adresi
Müşterinin iki adresi var. Adresin kendi kimliği yok, müşteri silinince adres de gitmeli, ayrı tabloya gerek yok.
Tablo Yapısı:
// --- Entity'ler ---
public class Address
{
public string Street { get; set; }
public string City { get; set; }
public string PostalCode { get; set; }
public string Country { get; set; }
}
public class Customer
{
public int Id { get; set; }
public string FullName { get; set; }
public string Email { get; set; }
public Address ShippingAddress { get; set; } // teslimat adresi
public Address BillingAddress { get; set; } // fatura adresi
}
// --- Config ---
public class CustomerConfiguration : IEntityTypeConfiguration<Customer>
{
public void Configure(EntityTypeBuilder<Customer> builder)
{
builder.ToTable("Customers");
builder.HasKey(c => c.Id);
builder.Property(c => c.FullName).IsRequired().HasMaxLength(150);
builder.Property(c => c.Email) .IsRequired().HasMaxLength(200);
builder.OwnsOne(c => c.ShippingAddress, addr =>
{
addr.Property(a => a.Street) .HasColumnName("Ship_Street") .HasMaxLength(200);
addr.Property(a => a.City) .HasColumnName("Ship_City") .HasMaxLength(100);
addr.Property(a => a.PostalCode).HasColumnName("Ship_PostalCode").HasMaxLength(10);
addr.Property(a => a.Country) .HasColumnName("Ship_Country") .HasMaxLength(50);
});
builder.OwnsOne(c => c.BillingAddress, addr =>
{
addr.Property(a => a.Street) .HasColumnName("Bill_Street") .HasMaxLength(200);
addr.Property(a => a.City) .HasColumnName("Bill_City") .HasMaxLength(100);
addr.Property(a => a.PostalCode).HasColumnName("Bill_PostalCode").HasMaxLength(10);
addr.Property(a => a.Country) .HasColumnName("Bill_Country") .HasMaxLength(50);
});
}
}
Oluşan SQL:
CREATE TABLE [Customers] (
[Id] INT IDENTITY(1,1) NOT NULL,
[FullName] NVARCHAR(150) NOT NULL,
[Email] NVARCHAR(200) NOT NULL,
-- ShippingAddress (Owned — aynı tabloda)
[Ship_Street] NVARCHAR(200) NULL,
[Ship_City] NVARCHAR(100) NULL,
[Ship_PostalCode] NVARCHAR(10) NULL,
[Ship_Country] NVARCHAR(50) NULL,
-- BillingAddress (Owned — aynı tabloda)
[Bill_Street] NVARCHAR(200) NULL,
[Bill_City] NVARCHAR(100) NULL,
[Bill_PostalCode] NVARCHAR(10) NULL,
[Bill_Country] NVARCHAR(50) NULL,
CONSTRAINT [PK_Customers] PRIMARY KEY CLUSTERED ([Id])
);
CREATE TABLE customers (
id INT GENERATED BY DEFAULT AS IDENTITY,
full_name VARCHAR(150) NOT NULL,
email VARCHAR(200) NOT NULL,
-- shipping_address (Owned — aynı tabloda)
ship_street VARCHAR(200) NULL,
ship_city VARCHAR(100) NULL,
ship_postal_code VARCHAR(10) NULL,
ship_country VARCHAR(50) NULL,
-- billing_address (Owned — aynı tabloda)
bill_street VARCHAR(200) NULL,
bill_city VARCHAR(100) NULL,
bill_postal_code VARCHAR(10) NULL,
bill_country VARCHAR(50) NULL,
CONSTRAINT pk_customers PRIMARY KEY (id)
);
Örnek veri:
| Id | FullName | Ship_Street | Ship_City | Ship_PostalCode | Ship_Country | Bill_Street | Bill_City | Bill_PostalCode | Bill_Country | |
|---|---|---|---|---|---|---|---|---|---|---|
| 1 | Ahmet Yılmaz | ahmet@mail.com | Bağdat Cad. No:42 | İstanbul | 34710 | Türkiye | Atatürk Blv. No:1 | Ankara | 06100 | Türkiye |
| 2 | Elif Demir | elif@mail.com | Kordon Boyu 15 | İzmir | 35220 | Türkiye | Kordon Boyu 15 | İzmir | 35220 | Türkiye |
OwnsMany — Sipariş kalemleri
Bir siparişin satır kalemleri (line items) vardır. Kalem, sipariş dışında anlam taşımaz.
ER Diyagramı:
public class OrderLineItem
{
public string ProductName { get; set; }
public int Quantity { get; set; }
public decimal UnitPrice { get; set; }
}
public class Order
{
public int Id { get; set; }
public string Reference { get; set; }
public ICollection<OrderLineItem> LineItems { get; set; }
}
public class OrderConfiguration : IEntityTypeConfiguration<Order>
{
public void Configure(EntityTypeBuilder<Order> builder)
{
builder.ToTable("Orders");
builder.HasKey(o => o.Id);
builder.OwnsMany(o => o.LineItems, item =>
{
item.ToTable("OrderLineItems"); // OwnsMany ayrı tablo zorunlu
item.WithOwner().HasForeignKey("OrderId");
item.Property(i => i.ProductName).IsRequired().HasMaxLength(200);
item.Property(i => i.Quantity) .IsRequired();
item.Property(i => i.UnitPrice) .HasPrecision(18, 2);
});
}
}
Oluşan SQL:
CREATE TABLE [Orders] (
[Id] INT IDENTITY(1,1) NOT NULL,
[Reference] NVARCHAR(MAX) NULL,
CONSTRAINT [PK_Orders] PRIMARY KEY CLUSTERED ([Id])
);
CREATE TABLE [OrderLineItems] (
[Id] INT IDENTITY(1,1) NOT NULL,
[OrderId] INT NOT NULL,
[ProductName] NVARCHAR(200) NOT NULL,
[Quantity] INT NOT NULL,
[UnitPrice] DECIMAL(18,2) NOT NULL,
CONSTRAINT [PK_OrderLineItems] PRIMARY KEY CLUSTERED ([Id]),
CONSTRAINT [FK_OrderLineItems_Orders] FOREIGN KEY ([OrderId]) REFERENCES [Orders]([Id]) ON DELETE CASCADE
);
CREATE TABLE orders (
id INT GENERATED BY DEFAULT AS IDENTITY,
reference TEXT NULL,
CONSTRAINT pk_orders PRIMARY KEY (id)
);
CREATE TABLE order_line_items (
id INT GENERATED BY DEFAULT AS IDENTITY,
order_id INT NOT NULL,
product_name VARCHAR(200) NOT NULL,
quantity INT NOT NULL,
unit_price NUMERIC(18,2) NOT NULL,
CONSTRAINT pk_order_line_items PRIMARY KEY (id),
CONSTRAINT fk_order_line_items_orders FOREIGN KEY (order_id) REFERENCES orders(id) ON DELETE CASCADE
);
Örnek veri:
Orders:
| Id | Reference |
|---|---|
| 1 | ORD-2025-001 |
| 2 | ORD-2025-002 |
OrderLineItems:
| Id | OrderId | ProductName | Quantity | UnitPrice |
|---|---|---|---|---|
| 1 | 1 | MacBook Pro 16" | 1 | 84999.99 |
| 2 | 1 | USB-C Hub | 2 | 1299.00 |
| 3 | 1 | Magic Mouse | 1 | 3499.00 |
| 4 | 2 | iPhone 15 Pro | 1 | 54999.00 |
JSON'a map etme (EF Core 7+)
Kalemler ayrı tablo yerine JSON sütununda saklanabilir.
builder.OwnsMany(o => o.LineItems, item =>
{
item.ToJson(); // ayrı tablo yerine Orders.LineItems sütununa JSON yazar
});
JSON formatında veri (Orders tablosu):
| Id | Reference | LineItems |
|---|---|---|
| 1 | ORD-2025-001 | [{"ProductName":"MacBook Pro","Quantity":1,"UnitPrice":84999.99},{"ProductName":"USB-C Hub","Quantity":2,"UnitPrice":1299.00}] |
Ne zaman Owned Entity, ne zaman ayrı tablo?
| Durum | Karar |
|---|---|
| Nesne her zaman sahibiyle birlikte sorgulanıyor | Owned Entity |
| Nesnenin geçmişi tutulacak (eski siparişin adresi değişmemeli) | Ayrı tablo |
| Aynı nesneyi birden fazla entity paylaşabilir | Ayrı tablo |
| Nesne tek başına listelenmeyecek | Owned Entity |