Derinlemesine yazılım eğitimleri için kanalımı takip edebilirsiniz...

EF Core 8 – Complex Types

Merhaba,

Bu içeriğimizde EF Core’da ki Owned Types özelliğine oldukça benzerlik gösteren ve biz geliştiriciler açısından bir entity içerisindeki property’lerin düzenlenmesine olanak tanıyan Complex Types özelliğini inceliyor olacağız.

Öncelikle Owned Types özelliğini hatırlayarak başlayalım… Owned Types, bir entity’nin parçası olarak sahip olduğu türleri ifade etmektedir. Bu türler kendi başlarına bir tabloya sahip değildirler. Sadece sahip oldukları entity ile birlikte veritabanı seviyesinde aynı tabloda yer almaktadırlar. Bunu daha iyi anlayabilmek için yukarıda giriş paragrafında referans ettiğim içeriğe göz atmanızda fayda görmekteyim.

Complex Types ise yine bir entity’nin parçası olarak sahip olduğu türleri ifade etmektedir lakin Owned Types’dan farklı olarak bazı yeteneklere sahiptir. Bu yeteneklerden en önemlisi bir alt türden üretilmiş instance’ın aynı anda farklı entity’ler tarafından kullanılabilmesidir. Çünkü bu davranış Owned Type’lar da geçersizdir.

Bunu en iyi şöyle bir örnek üzerinden izah edebiliriz;

public struct Address
{
    public string Street { get; set; }
    public string City { get; set; }
    public string State { get; set; }
    public string Country { get; set; }
    public string PostalCode { get; set; }
}

Diyelim ki elimizde Address adında yukarıdaki gibi bir nesnemiz olsun ve bu nesneyi aşağıdaki gibi Order ve ‘Customer’ entity’lerinde kullandığımızı varsayalım;

public class Order
{
    public int Id { get; set; }
    public required string Contents { get; set; }
    public required Address ShippingAddress { get; set; }
    public required Address BillingAddress { get; set; }
    public Customer Customer { get; set; } = null!;
}
public class Customer
{
    public int Id { get; set; }
    public required string Name { get; set; }
    public required Address Address { get; set; }
    public List<Order> Orders { get; } = new();
}

Şimdi yapılan bu çalışmayı Owned Types özelliğiyle yapılandıralım ve devamında aşağıdaki sorguyu çalıştırıp neticeyi gözlemleyelim;

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Customer>().OwnsOne(c => c.Address);
        modelBuilder.Entity<Order>().OwnsOne(o => o.ShippingAddress);
        modelBuilder.Entity<Order>().OwnsOne(o => o.BillingAddress);
    }
Customer customer = new()
{
    Name = "Fezayi",
    Address = new()
    {
        PostalCode = "1000",
        City = "Yozgat",
        Country = "Türkiye",
        State = "",
        Street = ""
    }
};
ApplicationDbContext context = new();
await context.Customers.AddAsync(customer);
await context.SaveChangesAsync();

Sonuç? Başarılı, öyle değil mi! Ama benzer mantıkla aşağıdaki sorguyu çalıştırırsak;

ApplicationDbContext context = new();

Customer? customer = await context.Customers.FindAsync(2);
customer.Orders.Add(new()
{
    Contents = "Kuru Üzüm",
    BillingAddress = customer.Address,
    ShippingAddress = customer.Address,
});

await context.SaveChangesAsync();

aşağıdaki hatayla karşılaşırız!

System.InvalidOperationException: ‘Cannot save instance of ‘Order.ShippingAddress#Address (Address)’ because it is an owned entity without any reference to its owner. Owned entities can only be saved as part of an aggregate also including the owner entity.’

İşte burada görüldüğü üzere, Owned Types özelliğinin yetersizliğiyle karşılaşıyoruz. Çünkü, bir entity parçasını farklı bir entity’de kullanmayı Owned Types özelliğiyle gerçekleştirmeye çalışırsak bu pek mümkün değildir!

Artık bu tarz bir duruma karşın Complex Types özelliğiyle müdahalede bulunabilmekte ve gayemizi gerçekleştirebilmekteyiz.

Bunun için ya model yapılandırmasını aşağıdaki gibi Complex Type olarak şekillendirmeli;

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Customer>()
            .ComplexProperty(e => e.Address);

        modelBuilder.Entity<Order>(b =>
        {
            b.ComplexProperty(e => e.BillingAddress);
            b.ComplexProperty(e => e.ShippingAddress);
        });
    }

ya da ComplexType attribute’undan faydalanmalıdır;

[ComplexType]
public class Address
{
    public string Street { get; set; }
    public string City { get; set; }
    public string State { get; set; }
    public string Country { get; set; }
    public string PostalCode { get; set; }
}

Böylece EF Core açısından ciddi bir davranışsal açığı kapatarak, ihtiyaç noktalarında kullanabilmekteyiz.

İlgilenenlerin faydalanması dileğiyle…
Sonraki yazılarımda görüşmek üzere…
İyi çalışmalar…

Bunlar da hoşunuza gidebilir...

3 Cevaplar

  1. c#f#vbjavajshtmlcss dedi ki:

    Hocam yine mükemmel bir makale olmuş. Elinize ve emeğinize sağlık.

  2. Hasan Ablak dedi ki:

    Hocam migrarions’da bir farklılık oluşacak mı model yapılandırmalarını değiştirdiğimiz de?

    • Gençay dedi ki:

      Evet, olacaktır. Her model üzerinde güncelleme yapıldığında bu migration’a yansıyacaktır, yansıtılmalıdırda.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir