Entity Framework Relationships (İlişkiler) ve Inheritance (Kalıtım/Miras) Stratejileri

Örnek projeyi github üzerinden indirmek veya görüntülemek için https://github.com/ayzdru/EntityFrameworkRelationshipsAndInheritanceStrategies adresine gidebilirsiniz.

İlişkisel veri tabanı yönetim sistemi (Relational Database Management System kısaca RDBMS)

1970 yılında Edgar Frank Codd tarafından önerilen, organizasyonu ilişkisel veri modeline dayanan verilerin tablolarda satır ve sütunlar hâlinde tutulduğu ve yüksek bir veri tutarlılığına sahip dijital veri tabanlarıdır.

İlişkisel veri tabanını çeşitli tablolar arasında organize edilmiş verilerden oluşan veri tabanı olarak açıklayabiliriz.

Bu tür veri tabanları arasında MSSQL, MySQL, Oracle ve PostgreSQL başta gelmektedir.

Neredeyse tüm ilişkisel veritabanı sistemleri, sorgulama ve veritabanının bakımı için dil olarak SQL(Structured Query Language) kullanmaktadırlar.

İlişkiler

Veritabanındaki tablolar arasındaki veriler, çeşitli anahtarlar vasıtası ile birbirlerine bağlanırlar. İlgili tablolarda, sütunlar arasında bir anahtar sütun yer alır. Bu anahtar sütun aracılığı ile birden çok tablo verileri birbiriyle bağlantı sağlayabilir ve herhangi bir sorgulamada birlikte görüntülenebilir.

One-to-one (Bire-Bir İlişki)

Bire bir ilişkiler, her iki tabloda benzersiz alanlar olmak kaydıya yapılan ilişki türüdür.

Örneğin: Aşağıda ki tablolarda olduğu gibi Students tablosu, Id kolonu ile StudentAddresses tablosu, StudentId kolonuyla bire-bir ilişki türü oluşturulmuştur. Burada amaç, Öğrenci oluştuktan sonra bu öğrenciye ait sadece bir tane Öğrenci Adresi kaydının olmasını sağlamaktır.

Entity classlarımızı oluşturmaya başlayalım.

public class Student
{
    public Guid Id { get; set; }
    public string Name { get; set; }
    //Bire bir ilişki
    public StudentAddress StudentAddress { get; set; }
}

public class StudentAddress
{
    public Guid Id { get; set; }
    public string Address { get; set; }
    public string City { get; set; }
    public string State { get; set; }
    public string Country { get; set; }
    //Bire bir ilişki
    public Guid StudentId{ get; set; }
    public Student Student { get; set; }
}

Entity Framework içerisinde Fluent API kullanarak bire bir ilişkimizi konfigüre edelim.

public class ApplicationDbContext : DbContext
{
        public DbSet<Student> Students { get; set; }
        public DbSet<StudentAddress> StudentAddresses { get; set;}
	public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options): base(options)
	{
	
	}
		
	protected override void OnModelCreating(ModelBuilder builder)        
	{			
		builder.Entity<StudentAddress>().HasOne(q => q.Student).WithOne(q => q.StudentAddress).HasForeignKey<StudentAddress>(q => q.StudentId);		
	}
}

One-to-many (Bire-Çok İlişki)

İlişkisel veritabanlarında en çok kullanılan ilişki türüdür. Bir kaydın normalizasyon kurallarına göre tekrarsız bir şekilde, başka tablolarla bir kolon aracılığıyla ilişkilendirilmesidir.

Örneğin: Classrooms ve Teachers adlı iki tablomuz olsun. Bir sınıfa bir öğretmeni ilişkilendirmek istediğimizde bire-çok ilişki türünü kullanabiliriz.

Entity sınıflarımızı oluşturalım.

public class Teacher
{
    public Guid Id { get; set; }
    public string FirstName { get; set; }
    public string LastName{ get; set; }
     //Bire çok ilişki
     private readonly List<Classroom> _classrooms = new List<Classroom>();
     public IReadOnlyCollection<Classroom> Classrooms => _classrooms.AsReadOnly();
}

public class Classroom
{
    public Guid Id { get; set; }
    public string Name{ get; set; }
    public string Description{ get; set; }
    //Bire çok ilişki
    public Guid TeacherId { get; set; }
    public Teacher Teacher { get; set; }    
}

Fluent API kullanarak bire-çok ilişkimizi konfigüre edelim.

public class ApplicationDbContext : DbContext
{
        public DbSet<Teacher> Teachers { get; set; }
        public DbSet<Classroom> Classrooms { get; set; }
	public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options): base(options)
	{
	
	}
		
	protected override void OnModelCreating(ModelBuilder builder)        
	{		
	    builder.Entity<Classroom>().HasOne(b => b.Teacher).WithMany(b => b.Classrooms).HasForeignKey(b => b.TeacherId);
            builder.Entity<Teacher>().Metadata.FindNavigation(nameof(Teacher.Classrooms)).SetPropertyAccessMode(PropertyAccessMode.Field);	
	}
}

Many-to-many (Çoka-Çok İlişki)

Çoka çok ilişki, bir tablodaki bir veya birden fazla alanın, başka bir tablodaki bir veya birden fazla alan ile ilişkisi bulunduğunda ortaya çıkar.

Örneğin: Elimizde Students, Classrooms, ClassroomStudents adlı 3 tablo olsun. Bir öğrenci birden fazla sınıfa dahil olabilir (bire-çok). Aynı şekilde bir sınıfta birden fazla öğrenci olacağından (bire-çok) ClassroomStudents adlı bir tablo açıp, çoka-çok ilişki kurabiliriz.

Entity sınıflarımızı oluşturalım.

public class Student
{
    public Guid Id { get; set; }
    public string FirstName { get; set; }
    public string LastName{ get; set; }
    //Çok çok ilişki
    private readonly List<ClassroomStudent> _classroomStudents = new List<ClassroomStudent>();
    public IReadOnlyCollection<ClassroomStudent> ClassroomStudents => _classroomStudents.AsReadOnly();
}

public class Classroom
{
    public Guid Id { get; set; }
    public string Name{ get; set; }
    public string Description{ get; set; }
    //Çok çok ilişki
    private readonly List<ClassroomStudent> _classroomStudents = new List<ClassroomStudent>();
    public IReadOnlyCollection<ClassroomStudent> ClassroomStudents => _classroomStudents.AsReadOnly();   
}
public class ClassroomStudent
{
    public Guid ClassroomId { get; set; }
    public Classroom Classroom { get; set; }
    public Guid StudentId { get; set; }
    public Student Student { get; set; }
}

Fluent API kullanarak çoka-çok ilişkimizi konfigüre edelim.

public class ApplicationDbContext : DbContext
{
        public DbSet<Student> Students { get; set; }
        public DbSet<ClassroomStudent> ClassroomStudents{ get; set; }
        public DbSet<Classroom> Classrooms { get; set; }
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options): base(options)
{
	
}		
protected override void OnModelCreating(ModelBuilder builder)        
{		
	  builder.Entity<ClassroomStudent>().HasOne(b => b.Classroom).WithMany(b => b.ClassroomStudents).HasForeignKey(b => b.ClassroomId);
          builder.Entity<ClassroomStudent>().HasOne(b => b.Student).WithMany(b => b.ClassroomStudents).HasForeignKey(b => b.StudentId);
          builder.Entity<ClassroomStudent>().HasKey(q => new { q.ClassroomId, q.StudentId });
          builder.Entity<Student>().Metadata.FindNavigation(nameof(Student.ClassroomStudents)).SetPropertyAccessMode(PropertyAccessMode.Field);
          builder.Entity<Classroom>().Metadata.FindNavigation(nameof(Classroom.ClassroomStudents)).SetPropertyAccessMode(PropertyAccessMode.Field);	
}
}

Entity Framework Inheritance

Entity Framework code first, her somut domain classlar için tablolar oluşturur. Ayrıca kalıtım/devralma/miras stratejelerini kullanarak, soyutlanmış domain classlar tasarlayabiliriz.

Entity Framework'te bir miras hiyerarşisini temsil etmek için üç yaklaşım vardır;

  • Table per Hierarchy (TPH)
  • Table per Concrete (TPC)
  • Table per Type (TPT)

Table per Hierarchy (TPH)

TPH, Kalıtım verilmiş bütün sınıflardaki alanları tek bir tabloda tutar. Tablodaki Discriminator kolonu nesnenin tipini belirler.

Entity classlarımızı oluşturalım.

public abstract class BaseCardEntity
{
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public long Number { get; set; }
        public int ExpiryMonth { get; set; }
        public int ExpiryYear { get; set; }
        public int Cvc { get; set; }
 }
public class CreditCard : BaseCardEntity
 {
        public decimal TotalEarnedPoint { get; set; }
        public bool IsVirtual { get; set; }
}
public class DebitCard : BaseCardEntity
{
        public string Iban { get; set; }
}

Fluent API kullanarak TPH stratejimizi konfigüre edelim.

 

public class ApplicationDbContext : DbContext
{
     public DbSet<BaseCardEntity> Cards { get; set; }
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options): base(options)
{
	
}		
protected override void OnModelCreating(ModelBuilder builder)        
{		
builder.Entity<CreditCard>().HasBaseType<BaseCardEntity>();
builder.Entity<DebitCard>().HasBaseType<BaseCardEntity>();		
}
}

Table-per-Concrete-Type (TPC)

TPC, türetilmiş her nesnenin veritabanında ayrı bir tablo olmasını sağlar.

Entity classlarımızı oluşturalım.

public abstract class BasePersonEntity
{
        public string FirstName { get; set; }
        public string LastName { get; set; }
}
public class Teacher : BasePersonEntity
{
        //Öğretmenler tablosu için eklemek istediğimiz ayrı alanları buraya yazıyoruz.
}
public class Student : BasePersonEntity
{
        //Öğrenciler tablosu için eklemek istediğimiz ayrı alanları buraya yazıyoruz.
}

Fluent API kullanarak TPC stratejimizi konfigüre edelim.

public class ApplicationDbContext : DbContext
{
public DbSet<Student> Students { get; set; }
public DbSet<Teacher> Teachers { get; set; }
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options): base(options)
{
	
}		
}

Table-per-Type (TPT)

Soyut nesnedeki ortak alanlar için tek bir tablo ve türetilmiş her nesne için ortak tablo ile ilişkilendirilmiş ayrı ayrı tablolar oluşturmamızı sağlar.

Entity interfacelerimizi ve classlarımızı oluşturalım.

 public class BaseCourse
 {
        public string Name { get; set; }
        public string Details { get; set; }
 }
public class Course : BaseCourse
    {
        //Bire çok ilişki
        private readonly List<OnlineCourse> _onlineCourses = new List<OnlineCourse>();
        public IReadOnlyCollection<OnlineCourse> OnlineCourses => _onlineCourses.AsReadOnly();

        //Bire çok ilişki
        private readonly List<OfflineCourse> _offlineCourses = new List<OfflineCourse>();
        public IReadOnlyCollection<OfflineCourse> OfflineCourses => _offlineCourses.AsReadOnly();
    }
 public class OfflineCourse : BaseCourse
    {
        public string Name { get; set; }
        public string Details { get; set; }
        public Guid CourseId { get; set; }
        public Course Course { get; set; }
        public string Address { get; set; }
    }
 public class OnlineCourse : BaseCourse
    {
        public string Name { get; set; }
        public string Details { get; set; }
        public Guid CourseId { get; set; }
        public Course Course { get; set; }
        public string Url { get; set; }
    }

Fluent API kullanarak TPT stratejimizi konfigüre edelim.

public class ApplicationDbContext : DbContext
{
public DbSet<Course> Courses { get; set; }
public DbSet<OnlineCourse> OnlineCourses { get; set; }
public DbSet<OfflineCourse> OfflineCourses { get; set; }
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options): base(options)
{
	
}
 protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<OfflineCourse>().HasOne(b => b.Course).WithMany(b => b.OfflineCourses).HasForeignKey(b => b.CourseId);

builder.Entity<OnlineCourse>().HasOne(b => b.Course).WithMany(b => b.OnlineCourses).HasForeignKey(b => b.CourseId);

builder.Entity<Course>().Metadata.FindNavigation(nameof(Course.OnlineCourses)).SetPropertyAccessMode(PropertyAccessMode.Field);
builder.Entity<Course>().Metadata.FindNavigation(nameof(Course.OfflineCourses)).SetPropertyAccessMode(PropertyAccessMode.Field);
}		
}

Örnek projeyi github üzerinden indirmek veya görüntülemek için https://github.com/ayzdru/EntityFrameworkRelationshipsAndInheritanceStrategies adresine gidebilirsiniz.

Sağlıcakla kalın..

 

 

 

Yorumlar kapalı