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

Entity Framework Core – Kalıtımsal Durumlardaki Table Per Type(TPT) ve Table Per Hierarchy(TPH) Davranış Modellemeleri

Merhaba,

Bu içeriğimizde EF Core’un kalıtımsal durumlardaki davranışlarından olan Table Per Type(TPT) ve Table Per Hierarchy(TPH) modellemelerini inceliyor olacağız.

Table Per Type(TPT) Nedir?

Veritabanı açısından bir tablodaki belirli kolonların bağımsız olarak birebir ilişki ile farklı tablolarda tutulmasıdır. Bunun için EF Core tarafında, merkezi tabloyu temsil eden bir türle/sınıfla kalıtımsal hiyerarşide olan entity sınıfları oluşturularak, ana sınıf/base class dahil tüm alt sınıflara karşılık bir tablo generate edilmesiyle sağlanan davranıştır.

Misal olarak aşağıdaki, bir ürün bilgisine karşılık oluşturulan sınıfların modellemesini ele alırsak eğer;

public class ApplicationUser
{
    public int Id { get; set; }
    public string Name { get; set; }
}
public class Student : ApplicationUser
{
    public string School { get; set; }
}
public class Teacher : ApplicationUser
{
    public string Branch { get; set; }
}

Görüldüğü üzere esasında tüm entity’ler bir tane kullanıcıya karşılık gelecek tek bir veri tutmak için tasarlanmıştır. Haliyle böyle bir durumda bu entity’leri context nesnesine aşağıdaki şekilde bildirmemiz Table Per Type davranışını sergilememizi sağlayacaktır.

public class TPTExampleDb : DbContext
{
    .
    .
    .

    public DbSet<ApplicationUser> Users { get; set; }
    public DbSet<Student> Students { get; set; }
    public DbSet<Teacher> Teachers { get; set; }
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        //Table Per Type(TPT)
        modelBuilder.Entity<Student>().ToTable("StudentUsers");
        modelBuilder.Entity<Teacher>().ToTable("TeacherUsers");
    }
}

Dikkat ederseniz eğer tüm entity’ler DbSet olarak eklenmiş lakin alt modeller ise OnModelCreating içerisinde ToTable olarak nitelendirilerek konfigüre edilmiştir. Bu çalışmanın neticesinde migration oluşturup veritabanını update edersek aşağıdaki gibi bir tablo yapılanmasının kurulduğunu göreceğiz.Entity Framework Core - Kalıtımsal Durumlardaki Table Per Type(TPT) ve Table Per Hierarchy(TPH) Davranış ModellemeleriUnutulmamalıdır ki, tüm alt sınıflara karşılık Table Per Type davranışında birebir ilişkide tablolar oluşturulabilmesi için yukarıdaki gibi ToTable ile isimlendirilmeleri gerekmektedir. Bu zaruridir.

Peki hoca, Table Per Type davranışında CRUD işlemlerini nasıl yapacağız? şeklinde sorunuzu duyar gibiyim… Tabi ki de bunu göstermeden geçmeyeceğiz 🙂

Bunun için tek yapılması gereken CRUD işlemleri sürecinde sadece alt sınıfları kullanmaktır.

using TPTExampleDb context = new();
await context.Students.AddAsync(new() { Name = "Hilmi", School = "Yozgat Ticaret Meslek Lisesi" });
await context.Students.AddAsync(new() { Name = "Furkan", School = "Kaşıkçı Torna Tesviye Meslek Lisesi" });
await context.Students.AddAsync(new() { Name = "Ali", School = "Bülbülderesi Anadolu Meslek Lisesi" });
await context.Teachers.AddAsync(new() { Name = "Emrah", Branch = "Matematik" });
await context.Teachers.AddAsync(new() { Name = "Engin", Branch = "Müzik" });
await context.Teachers.AddAsync(new() { Name = "Erol", Branch = "Dayak" });
await context.SaveChangesAsync();

Entity Framework Core - Kalıtımsal Durumlardaki Table Per Type(TPT) ve Table Per Hierarchy(TPH) Davranış ModellemeleriDikkat ederseniz eğer verileri ‘Id’ kolonu üzerinden birebir olan bir ilişkide tutmaktadır.
Aynı şekilde veri okumak istediğinizde de ilgili alt sınıfı sorgulayabilirsiniz.
Entity Framework Core - Kalıtımsal Durumlardaki Table Per Type(TPT) ve Table Per Hierarchy(TPH) Davranış Modellemeleri

Table Per Type yaklaşımında hiyerarşik olarak ne kadar entity varsa o kadar derinlikte birebir ilişkili tablo oluşturulacaktır.

Ayrıca son olarak ‘Users’ DbSet’i üzerinden sorgulama yaparken sorguyu alt türe indirgemek istiyorsanız eğer aşağıdaki gibi OfType metodundan istifade edebilirsiniz.

var users = await context.Users.ToListAsync();
var students = users.OfType<Student>().ToList();
var teachers = users.OfType<Teacher>().ToList();

Table Per Hierarchy (TPH) Nedir?

Bu davranışta ise hiyerarşi içindeki tüm entity’ler için tek bir tablo oluşturulmakta ve bu tabloda veriler tutulurken ortak olan yani base class’dan gelen alanlar doldurulmakta, olmayan alanlar için nullable şartı gelmektedir. Yani bir başka deyişle, veritabanındaki bir tablo EF Core tarafında birden fazla entity’nin bütünsel haline karşılık gelmektedir diyebiliriz.

Bu davranışa da örnek mahiyetinde yine yukarıdaki entity’lerden yola çıkarsak eğer context nesnesi aşağıdaki gibi tasarlanacaktır;

public class TPHExampleDb : DbContext
{
    .
    .
    .

    public DbSet<ApplicationUser> Users { get; set; }
    public DbSet<Student> Students { get; set; }
    public DbSet<Teacher> Teachers { get; set; }
}

Dikkat ederseniz Table Per Hierarchy davranışı için OnModelCreating içerisinde herhangi bir işlem yapmaya gerek yoktur. Hiyerarşik olarak tasarlanan tüm entity’lerin direkt olarak DbSet türünde tanımlanması yeterlidir. Bu tanımlamadan sonra OnModelCreating içerisinde ToTable işlemi yaparsak bu Table Per Type davranışına yönlenecektir.

Bu vaziyette migration oluşturup, veritabanını güncellersek eğer aşağıdaki gibi bir tablo oluşturulacaktır.
Entity Framework Core - Kalıtımsal Durumlardaki Table Per Type(TPT) ve Table Per Hierarchy(TPH) Davranış ModellemeleriBu tabloda görüldüğü üzere üst sınıfla birlikte tüm alt sınıflardaki property’ler tek bir tabloya kolon olarak eklenmiş vaziyettedir.

Şimdi buradan anlaşılan, ister ‘Student’ ister ‘Teacher’ verileri gelsin her ikisi de bu oluşturulan ‘Users’ tablosuna eklenecektir. Hal böyleyken gelecek olan verinin hangi entity’e özel olduğunu ayrıştırabilmek için EF Core tarafından eklenen Discriminator kolonunu görmekteyiz. Bu kolonda gelen verinin hangi entity tarafından kayıt edildiğine dair bilgi tutulacaktır.

Şöyle ki;

using TPHExampleDb context = new();
await context.Students.AddAsync(new() { Name = "Hilmi", School = "Hacı Hüseyin İmam Hatip Lisesi" });
await context.Students.AddAsync(new() { Name = "Rauf", School = "Laylaylom Galiba Sana Göre Sevmeler Lisesi" });
await context.Teachers.AddAsync(new() { Name = "Emine", Branch = "Fizik" });
await context.Teachers.AddAsync(new() { Name = "Muiddin", Branch = "Edebiyat" });
await context.SaveChangesAsync();

şeklinde veri eklediğimizde aşağıdaki ekran görüntüsünde olduğu gibi hangi datanın hangi entity’den geldiğini ‘Discriminator’ kolonuna eklemekte ve ilgili türün dışındaki alana null karşılığını vermektedir.Entity Framework Core - Kalıtımsal Durumlardaki Table Per Type(TPT) ve Table Per Hierarchy(TPH) Davranış ModellemeleriSorgularken de yine direkt türler üzerinden sorgulama yapılabileceği gibi

var students = await context.Students.ToListAsync();
var teachers = await context.Teachers.ToListAsync();

OfType fonksiyonu eşliğinde de alt türe indirgeyerek sorgulayabilirsiniz.

var students =  context.Users.OfType<Student>();
var teachers =  context.Users.OfType<Teacher>();

Her iki şekilde de ilgili tablodan entity türüne özgü veriler ayrıştırılarak, getirilecektir.

Table Per Hierarchy yaklaşımında hiyerarşik olarak entity derinliği fark etmeksizin tüm türler tek bir tabloya yansıtılacak ve aralarındaki fark ‘Discriminator’ kolonu sayesinde ayrıştırılacaktır.

Nihai olarak,
Entity’ler arasındaki kalıtımsal durum söz konusuyken; Table Per Type, her tip/entity’e karşılık bir tablo davranışı sergilerken, Table Per Hierarchy ise her hiyerarşiye karşılık bir tablo davranışını sergilemektedir.

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

Not : Makale sürecinde kullanılan kodların kaynaklarını aşağıdaki Githup adreslerinden edinebilirsiniz.
EF Core Table Per Type
EF Core Table Per Hierarchy

Bunlar da hoşunuza gidebilir...

8 Cevaplar

  1. A dedi ki:

    Hocam merhaba sizi severek izliyoruz, emeğiniz için çok teşekkür ederim.Hocam, entity videoları gelicekmi? Teşekkürler.

  2. Baturhan Kahraman dedi ki:

    Emeğiniz için teşekkürler. Çok bilgilendirici.
    Full text search nasıl yapılıyor derived tabloları da dahil ederek bir bilginiz var mı acaba ?

  3. can dedi ki:

    Ellerinize sağlık hocam. Böyle bir Türkçe kaynak olması çok çok kıymetli.

  4. Mesut dedi ki:

    Hocam TPT’yi anladım. Ancak bir mantık sorunu var
    Users=> Students, Teachers olarak düşünelim.

    Kayıt olan kişi paket şeçtikten sonra nitelik kazandığını kabul edersek.
    Users => Id = 3, Name = “Ali” ve henüz alt tablolar yok

    Eğer öğrenci ise =>
    context.Students.Add(new Student() { Id = 3 });

    Cannot insert explicit value for identity column in table ‘Users’ when IDENTITY_INSERT is set to OFF.
    Hatası geliyor, Sil ve ekle mantıklı bir yöntem olamaz.

    Update kullansam bu sefer olmayan kayıt nasıl güncelenebilir mi?
    “Olsun yinede deneyim” dedim

    var student = TypeConversion.Conversion(user);
    context.Students.Update(student);
    

    Microsoft.EntityFrameworkCore.DbUpdateConcurrencyException: ‘The database operation was expected to affect 1 row(s), but actually affected 0 row(s); data may have been modified or deleted since entities were loaded. See http://go.microsoft.com/fwlink/?LinkId=527962 for information on understanding and handling optimistic concurrency exceptions.’

    Manuel sql cümleciği çalıştırmak zorunda mıyım?

  5. jafar dedi ki:

    Çox qiymətli informasiya mənbəyisiniz🙌🏽

  6. Adil dedi ki:

    Mükemmelsin hocam!

  1. 02 Ekim 2022

    […] Per Concrete Type modellemesinin ne olduğunu ortaya koyabilmek için öncelikle Table Per Type (TPT) ve Table Per Hierarchy (TPH) davranış modellemelerini hatırlayarak […]

Bir yanıt yazın

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