Merhaba,
Bu içeriğimizde EF Core 7 ile gelecek olan Table Per Concrete Type(TPC) davranış modellemesini inceliyor olacağız.
Table Per Concrete Type (TPC) Nedir?
Table 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 başlayalım.
TPT, merkezi bir tabloyu temsil edecek olan bir sınıf ile kalıtımsal olarak ilişkide olan entity sınıflarının her birine karşılık bir tablonun generate edilmesini sağlayan ve bunlar arasında birebir ilişki kuran kalıtımsal davranış modellemesidir. Yani anlayacağınız, özünde hiyerarşideki her sınıfa karşılık tablo oluşturmakta ve oluşturulan bir üst sınıfla birebir ilişki sağlamaktadır.
abstract public class ApplicationUser
{
public int Id { get; set; }
public string Name { get; set; }
}
abstract public class Student : ApplicationUser
{
public string School { get; set; }
}
public class PostGraduate : Student
{
public string Section { get; set; }
}
public class Teacher : ApplicationUser
{
public string Branch { get; set; }
}
şeklinde kalıtımsal olarak tasarlanmış olan entity’leri ele alırsak eğer bunların context üzerinde aşağıdaki gibi konfigüre edilmeleri neticesinde
class ApplicationDbContext : DbContext
{
public DbSet<ApplicationUser> ApplicationUsers { get; set; }
public DbSet<Student> Students { get; set; }
public DbSet<PostGraduate> PostGraduates { get; set; }
public DbSet<Teacher> Teachers { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Student>().ToTable("StudentsUsers");
modelBuilder.Entity<PostGraduate>().ToTable("PostGraduateUsers");
modelBuilder.Entity<Teacher>().ToTable("TeacherUsers");
}
.
.
.
}
veritabanına yansımaları aşağıdaki gibi olacaktır.
await context.PostGraduates.AddAsync(new() { Name = "Gençay", School = "Aksaray Üni.", Section = "Yönetim Bilişim Sistemleri" });
await context.PostGraduates.AddAsync(new() { Name = "Okan", School = "Ankara Üni.", Section = "Matematik" });
await context.PostGraduates.AddAsync(new() { Name = "Rakıf", School = "ODTÜ", Section = "Felsefe" });
await context.Teachers.AddAsync(new() { Name = "Bülent", Branch = "Matematik" });
await context.SaveChangesAsync();
Ve bu tasarımda yukarıdaki gibi herhangi bir entity üzerinden kayıt girildiği taktirde tüm veriler kendisine karşılık tabloya gelecek şekilde aşağıdaki görseldeki gibi ilişkisel olarak eklenecektir.
TPH ise hiyerarşideki tüm entity’ler için tek bir tablo oluşturmakta ve bunlar arasındaki ayrım için bir Discriminator kolonu kullanmaktadır.
Yukarıdaki örnekte sunulan aynı entity’leri context üzerinde herhangi bir konfigürasyona tabi tutmadan direkt migrate edersek eğer aşağıdaki gibi beklenen tablonun inşa edildiğini göreceğiz.
Şimdi TPT ve TPH davranış modellemelerini hatırladığımıza göre artık TPC davranışının ne olduğuna odaklanabiliriz.
TPC‘ye, TPT stratejisine benzeyen lakin genel olarak performanslı davranışını sergileyen versiyonudur diyebiliriz. TPT, kalıtımsal hiyerarşideki tüm türler için tablo oluştururken TPC sade ve sadece concrete yani somut sınıflar için tablo oluşturmaktadır. Burada örneğimize bakarsanız eğer ‘ApplicationUser’ ve ‘Student’ abstract class olduklarından dolayı TPC‘de bunlara karşılık bir tablo üretilmeyecek dolayısıyla migrate neticesinde üretilen tablo yapılanması aşağıdaki gibi olacaktır.
Peki, TPC’nin yapılandırmasını nasıl sağlayacağız? diye sorarsanız eğer bunun için aşağıdaki gibi bir konfigürasyon yeterli olacaktır.
class ApplicationDbContext : DbContext
{
.
.
.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<ApplicationUser>().UseTpcMappingStrategy();
modelBuilder.Entity<Student>().UseTpcMappingStrategy();
}
.
.
.
}
Görüldüğü üzere UseTpcMappingStrategy metodu sayesinde bildirimde bulunulan entity’nin davranışını TPC olarak yapılandırabiliyoruz. Burada bir tek TPC için değil UseTphMappingStrategy ve UseTptMappingStrategy fonksiyonları aracılığıyla diğer davranışlar için de yapılandırmaları gerçekleştirebiliyoruz. (TPH‘ın varsayılan strateji olduğunu unutmayın! Ekstradan bir konfigürasyon gerektirmez.)
TPC’de Primary Key Problemi
Şimdi TPC davranışının söz konusu olduğu durumda yukarıdaki satırlarda örnek olarak vermiş olduğumuz insert işlemini uygularsak eğer aşağıdaki gibi hatayla karşılaşacağız.
class ApplicationDbContext : DbContext
{
.
.
.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.HasSequence("ApplicationUserIds");
modelBuilder.HasSequence("StudentIds");
modelBuilder.Entity<ApplicationUser>()
.UseTpcMappingStrategy()
.Property(p => p.Id)
.HasDefaultValueSql("NEXT VALUE FOR [ApplicationUserIds]");
modelBuilder.Entity<Student>()
.UseTpcMappingStrategy()
.Property(p => p.Id)
.HasDefaultValueSql("NEXT VALUE FOR [StudentIds]");
}
.
.
.
}
Böylece tekrardan TPC‘nin söz konusu olduğu durumlarda insert işlemine kalkarsak eğer veriler aşağıdaki gibi veritabanına kaydedilecektir.
Hangi Durumlarda Hangi Stratejileri Kullanmalıyız?
Entity’ler arası kalıtımsal durumlarda TPH stratejisi varsayılandır. Hiyerarşi ne kadar genişse büyüklük açısından doğru orantıda bir tablo inşa edilir ve genellikle tablonun birçok sütunu boş kalmaktadır. Ama bu durum sorgu performansları açısından bir sorun teşkil etmeyecek, her daim sorgulama tek bir tablo üzerinden gerçekleştirileceği için gayet verimli bir süreç geçirilecektir.
TPT ise nadiren iyi bir seçim olacaktır. Nihayetinde normalizasyona daha uygun bir tasarım sunmaktır. Ama bir entity’e karşılık tüm verileri birden fazla tabloya bölmesi veri sorgulama sürecinde ekstradan join maliyetlerine sebep olacaktır.
TPC stratejisi ise belirli concrete type’dan olan entity’ler için bilgilerin her daim tek bir tabloda tutulmasını sağlanmaktadır. Bu açıdan TPT‘nin gelişmiş versiyonu olarak düşünülebilir. Abstract türlerin kullanıldığı hiyerarşik tasarımlarda tercih edilebilir. TPH‘ye nazaran da hiçbir tabloda boş sütun barındırmayacağı ve Discriminator kolonu olmayacağı için boyut olarak daha az yer kaplayacaktır.
Eğer ki, yapılan çalışmada base türler üzerinden çalışmalar sergilenecekse ve base türün altındaki sınıflar üzerinde sorgulama işlemleri gerçekleştirilecekse TPH‘ı düşünebilirsiniz.
EF Core 7’yi Sağlıklı Kullanabilmek İçin
Son olarak EF Core 7 ile bu makaledeki içeriğe eşlik edebilmek için projenizde bulunması gereken salt kütüphaneleri bildirip içeriğimizi noktayalım.
İlgilenenlerin faydalanması dileğiyle…
Sonraki yazılarımda görüşmek üzere…
İyi çalışmalar…
