Entity Framework Core - Fluent API ve İlişkiler Rehberi
Entity Konfigürasyonu
Temel Entity Yapısı
modelBuilder.Entity<EntityName>(entity =>
{
});
Row Version (Optimistik Kilitleme)
entity.Property(x => x.RowVersion)
.IsRowVersion();
Discriminator (Kalıtım Kullanırken)
modelBuilder.Entity<Note>()
.HasDiscriminator<string>("NoteType")
.HasValue<TextNote>("Text")
.HasValue<AudioNote>("Audio");
Seed Data (Başlangıç Verileri)
entity.HasData(
new Category { Id = 1, Name = "General" }
);
Entity Configuration Dosyalarını Otomatik Yükleme
modelBuilder.ApplyConfigurationsFromAssembly(
typeof(AppDbContext).Assembly
);
Fluent API - Detaylı Rehber
1. IsRequired() - Zorunlu Alan
Açıklama: Bu özellik, ilgili alanın zorunlu olduğunu belirtir. Yani, bu alanın null olmasına izin verilmez. Veritabanına kaydedilirken bu alanın boş bırakılması engellenir.
entity.Property(x => x.Title).IsRequired();
2. HasMaxLength() - Maksimum Uzunluk
Açıklama: Bu özellik, bir alan için maksimum karakter uzunluğunu belirtir. Alanın içeriği bu uzunluğu aşarsa, bir hata alırsınız.
entity.Property(x => x.Title).HasMaxLength(200);
3. HasColumnType() - Sütun Veri Türü
Açıklama: Veritabanındaki sütunun veri türünü belirler. Örneğin, bir tarih alanının "datetime2" olmasını sağlayabilirsiniz.
entity.Property(x => x.CreatedAt).HasColumnType("datetime2");
4. HasDefaultValue() - Varsayılan Değer
Açıklama: Bu özellik, bir alan için varsayılan değeri belirler. Eğer bu alan null ise, veritabanı bunu otomatik olarak bu varsayılan değerle doldurur.
entity.Property(x => x.IsActive).HasDefaultValue(true);
5. HasDefaultValueSql() - SQL ile Varsayılan Değer
Açıklama: Bu, veritabanı için bir SQL sorgusu ile varsayılan değeri belirler. Örneğin, bir tarih alanına GETDATE() gibi dinamik bir SQL fonksiyonu atanabilir.
entity.Property(x => x.CreatedAt).HasDefaultValueSql("GETDATE()");
6. HasPrecision() - Sayısal Hassasiyet
Açıklama: Sayısal bir alan için hassasiyet (precision) ve ölçek (scale) değerini belirler. Precision, toplam rakam sayısını, scale ise virgülden sonraki basamak sayısını belirtir.
entity.Property(x => x.Price).HasPrecision(18, 2);
7. IsRowVersion() - Satır Versiyonu
Açıklama: Bu, optimistik kilitleme için kullanılan bir özelliktir. Veritabanı satırının versiyon numarasını tutarak, verilerin değiştirilip değiştirilmediğini kontrol etmek için kullanılır.
entity.Property(x => x.RowVersion).IsRowVersion();
8. HasColumnName() - Sütun Adı
Açıklama: Bu özellik, veritabanındaki sütunun adını değiştirir. Varsayılan olarak property adıyla eşleşen bir sütun adı kullanılır, ancak burayla o adı değiştirebilirsiniz.
entity.Property(x => x.Title).HasColumnName("NoteTitle");
9. HasConversion() - Veri Dönüşümü
Açıklama: Veritabanındaki veri türünü dönüştürmek için kullanılır. Örneğin, bir enum değerini string olarak veritabanına kaydetmek için kullanılabilir.
entity.Property(x => x.Status).HasConversion<string>();
10. IsConcurrencyToken() - Eşzamanlılık Belirteci
Açıklama: Bu özellik, optimistik kilitleme için bir işaretçi görevi görür. Özellikle aynı anda birden fazla kullanıcı veriyi değiştirmeye çalıştığında, bu alan bir "concurrency token" (eşzamanlılık belirteci) olarak kullanılabilir.
entity.Property(x => x.RowVersion).IsConcurrencyToken();
11. HasAlternateKey() - Alternatif Anahtar
Açıklama: Bu, bir alanı alternatif anahtar olarak tanımlar. Yani bu alanda tekrar eden değerler kabul edilmez, ancak bu alan birincil anahtar (primary key) değildir.
entity.HasAlternateKey(x => x.Email);
12. UsePropertyAccessMode() - Property Erişim Modu
Açıklama: Bu özellik, bir property'ye nasıl erişileceğini belirtir. PropertyAccessMode.Field kullanıldığında, property'ye doğrudan field üzerinden erişilir, PropertyAccessMode.Property ise özellik aracılığıyla erişimi belirtir.
entity.Property(x => x.Title).UsePropertyAccessMode(PropertyAccessMode.Field);
13. HasComment() - Açıklama
Açıklama: Veritabanı sütununa bir açıklama ekler. Bu açıklama genellikle veritabanı şemasının belgelenmesinde veya veri yönetiminde faydalıdır.
entity.Property(x => x.Title).HasComment("This is the title of the note");
14. HasCheckConstraint() - Kontrol Kısıtlaması
Açıklama: Veritabanı sütunu için bir kontrol kısıtlaması (check constraint) ekler. Örneğin, bir sütunun değeri belirli bir koşulu karşılamalıdır.
entity.Property(x => x.Age).HasCheckConstraint("CK_Age", "Age >= 18");
15. HasValueComparer() - Değer Karşılaştırıcı
Açıklama: Bir property'nin değerlerini karşılaştırmak için bir ValueComparer sağlar. Bu özellik, özellikle koleksiyonlar gibi karmaşık veri türleriyle çalışırken kullanılır.
entity.Property(x => x.Tags).HasValueComparer(new ValueComparer<List<string>>(
(c1, c2) => c1.SequenceEqual(c2),
c => c.Aggregate(0, (i, v) => i ^ v.GetHashCode())
));
16. IsUnicode() - Unicode Desteği
Açıklama: Bu, bir metin sütununun Unicode olup olmayacağını belirler. Eğer true olarak ayarlanırsa, sütun Unicode karakterleri kabul eder (örn. Türkçe, Çince, vb.).
entity.Property(x => x.Title).IsUnicode(true);
17. IsFixedLength() - Sabit Uzunluk
Açıklama: Bu, bir metin sütununun sabit uzunlukta olup olmayacağını belirtir. Eğer true olarak ayarlanırsa, veri sabit uzunlukta saklanır.
entity.Property(x => x.Code).IsFixedLength(true);
18. IsKey() - Birincil Anahtar
Açıklama: Bu, property'yi birincil anahtar (primary key) olarak belirler. Genellikle, HasKey ile birlikte kullanılır ve bir entity'nin birincil anahtarını belirtir.
entity.Property(x => x.Id).IsKey();
19. IsUnique() - Benzersiz
Açıklama: Bu özellik, bir alanın benzersiz olmasını (unique) sağlar. Yani, aynı değeri birden fazla kez içeremez.
entity.Property(x => x.Email).IsUnique();
20. IsIndex() - İndeks Oluştur
Açıklama: Bu özellik, belirtilen alan için veritabanında bir indeks oluşturur. Genellikle sorgu performansını artırmak için kullanılır.
entity.Property(x => x.Title).IsIndex();
21. ToTable() - Tablo Adı Belirleme
Açıklama: Entity'nin karşılık geldiği tablo adını belirtir.
entity.ToTable("Notes");
22. ToTable() - Tablo Adı ve Şema
Açıklama: Entity'nin tablo adını ve veritabanı şemasını belirtir.
entity.ToTable("Notes", "dbo");
23. OwnsOne() - Sahip Olunan (Owned) Tip
Açıklama: OwnsOne(), bir entity'nin içinde başka bir entity'nin sahip olunan (owned) tür olduğunu belirtmek için kullanılır. Sahip olunan türler, ana entity'ye bağlı ve onun bir parçası olarak kabul edilir. Bu türler veritabanında ayrı bir tablo oluşturmazlar, ana entity ile aynı tabloyu paylaşırlar.
Sahip olunan türler, genellikle entity'ye ait kompleks veya bağımsız veri yapılarıdır. Bir entity'nin bir özelliği, başka bir entity'yi sahiplenebilir (owned entity).
Örnek Senaryo: Bir User entity'sinin içinde Metadata adında sahip olunan bir entity olabilir.
entity.OwnsOne(x => x.Metadata, m =>
{
// Sahip olunan entity'nin özelliklerini yapılandır
m.Property(p => p.CreatedBy).HasMaxLength(100);
});
Açıklama: Bu örnekte, Metadata adlı bir sahip olunan entity, User entity'sinin bir parçası olarak tanımlanıyor. m.Property(p => p.CreatedBy).HasMaxLength(100): Sahip olunan Metadata entity'sindeki CreatedBy özelliği için maksimum 100 karakter uzunluğunda bir sınırlama ekler.
24. Property<T>() - Veri Tipini Belirlemek
Açıklama: .Property<T>(), belirli bir entity'nin bir özelliği için veri tipi belirlemek amacıyla kullanılır. Özellikle sütun tipi ve veri tipi ile ilgili daha fazla kontrol sağlamak için kullanılır. Burada, DateTime türünü belirtiyoruz.
entity.Property<DateTime>("CreatedAt");
Açıklama: Bu yapı, CreatedAt adlı bir DateTime özelliği oluşturur. Bu özellik, veritabanında datetime türünde bir sütun olarak karşılık bulacaktır. DateTime türü, genellikle zaman damgası (timestamp) gibi tarih ve saat bilgilerini saklamak için kullanılır.
25. HasQueryFilter() - Global Filtre Uygulamak
Açıklama: .HasQueryFilter(), global sorgu filtreleri eklemek için kullanılır. Bu filtre, veritabanında silinmiş ya da aktif olmayan verileri gizlemek gibi durumlar için çok kullanışlıdır.
Bu metod, belirli bir entity için otomatik olarak uygulanan bir filtre ekler. Örneğin, veritabanındaki silinmiş verilerin her sorguya dahil edilmesini engeller.
Örnek Senaryo: Eğer bir entity'de bir IsDeleted alanı varsa, silinmiş kayıtları her zaman sorgularda hariç tutmak isteyebilirsiniz.
entity.HasQueryFilter(x => !x.IsDeleted);
Açıklama: Burada, HasQueryFilter(x => !x.IsDeleted) ifadesi, IsDeleted özelliği false olmayan (yani silinmiş) verileri gizler. Bu, uygulamanın tüm sorgularında otomatik olarak geçerli olacak bir filtre uygular. IsDeleted gibi bir bayrak (flag) kullanarak, verileri "silinmiş" olarak işaretleyebilir ve bu silinmiş verilerin istemsiz bir şekilde kullanıcıya gösterilmesini engelleyebilirsiniz.
Silme Davranışı (Delete Behavior)
1. DeleteBehavior.Cascade
Açıklama: Cascade (kaskad) silme, bir kayıt silindiğinde, ilişkili olan tüm kayıtların da otomatik olarak silinmesini sağlar. Yani, ana kaydın silinmesiyle birlikte bağlı olduğu tüm veriler de silinir.
Örnek: Bir Order silindiğinde, ona bağlı tüm OrderItems de silinebilir.
entity.HasOne(x => x.Order)
.WithMany(o => o.OrderItems)
.OnDelete(DeleteBehavior.Cascade);
2. DeleteBehavior.Restrict
Açıklama: Restrict (kısıtlama) silme, bir kayıt silinmeye çalışıldığında, eğer bu kayıtla ilişkili başka bir kayıt varsa, silmeyi engeller. Yani, bir kaydın silinmesi yalnızca onunla ilişkili başka veriler yoksa mümkün olur.
Örnek: Bir Product silinmeye çalışıldığında, bu Product ile ilişkili OrderItems varsa, silme işlemi engellenir. Bu durumda, ilişkili veriler önce temizlenmeli veya güncellenmelidir.
entity.HasOne(x => x.Product)
.WithMany(p => p.OrderItems)
.OnDelete(DeleteBehavior.Restrict);
3. DeleteBehavior.NoAction
Açıklama: NoAction (hiçbir işlem yapma) silme, yabancı anahtar kısıtlamasına uygun olarak, herhangi bir işlem yapmaz. Bu, veritabanı düzeyinde veritabanının otomatik olarak bir işlem yapmasını engeller, ancak veritabanı tarafında başka kısıtlamalar (örneğin, ON DELETE NO ACTION) olabilir.
Örnek: Eğer bir Customer kaydı silinmeye çalışıldığında, NoAction seçilirse, veri kaybı yaşanmaması için veritabanı başka şekilde müdahale etmeyebilir, fakat bu genellikle Restrict'in benzeridir, ancak dış bir müdahale gerekebilir.
entity.HasOne(x => x.Customer)
.WithMany(c => c.Orders)
.OnDelete(DeleteBehavior.NoAction);
4. DeleteBehavior.SetNull
Açıklama: SetNull silme, bir kayıt silindiğinde, ilişkili olduğu diğer kayıtların yabancı anahtar alanlarını null olarak ayarlar. Bu, ilişkili verilerin silinmesini engeller, ancak ilişkili olan foreign key alanı null değerine atanır.
Örnek: Bir Order kaydı silindiğinde, ona bağlı olan CustomerId foreign key'i null olarak ayarlanabilir. Ancak Order kaydının kendisi silinmez.
entity.HasOne(x => x.Customer)
.WithMany(c => c.Orders)
.OnDelete(DeleteBehavior.SetNull);
İlişki Türleri (Relationship Types)
- One-to-One: Her iki tarafta yalnızca bir kayıtla ilişkilidir.
- One-to-Many: Bir entity, diğer birden fazla entity ile ilişkilidir.
- Many-to-One: Birden fazla entity, aynı entity ile ilişkilidir.
- Many-to-Many: Birden fazla entity, diğer birden fazla entity ile ilişkilidir.
- Self-Referencing: Bir entity, kendi türüyle ilişkili olabilir (ağaç yapıları vb).
- Shared Primary Key: İki entity aynı birincil anahtarı paylaşır.
- Many-to-Many (Junction Table): Geçici bir ilişki tablosu kullanarak kurulur.
1. One-to-One (Birbirine Bağlı Tekil İlişki)
Açıklama: Bu ilişki türü, bir entity'nin bir başka entity ile yalnızca bir kez ilişkili olduğu durumu belirtir. Bu ilişkiyi WithOne() ile belirtebilirsiniz.
Örnek: Bir User'ın bir Profile'ı olabilir (Profile, User'a ait bir ilişkidir).
entity.HasOne(x => x.Profile)
.WithOne(p => p.User)
.HasForeignKey<UserProfile>(p => p.UserId);
2. One-to-Many (Birden Bir'e İlişki)
Açıklama: Bu, bir entity'nin bir başka entity ile birden fazla kez ilişkili olduğu durumu belirtir. Bu tür ilişkiyi, HasMany() ve WithOne() metodlarıyla tanımlarsınız.
Örnek: Bir User birden fazla Note'a sahip olabilir.
entity.HasOne(x => x.User)
.WithMany(u => u.Notes)
.HasForeignKey(x => x.UserId);
3. Many-to-One (Çoktan Bire İlişki)
Açıklama: Many-to-One ilişkisi, One-to-Many ilişkisinin tam tersidir. Burada birden fazla entity, aynı ana entity ile ilişkili olabilir. Bu ilişki, HasOne() ve WithMany() metotlarıyla tanımlanır.
Örnek: Bir Note bir User'a ait olabilir. Yani, çok sayıda Note tek bir User'a bağlanır.
entity.HasMany(x => x.Notes)
.WithOne(n => n.User)
.HasForeignKey(n => n.UserId);
4. Many-to-Many (Çoktan Çoğa İlişki)
Açıklama: Birden fazla entity, diğer birden fazla entity ile ilişkili olduğunda bu ilişki kullanılır. EF Core 5 ve sonrasında Many-to-Many ilişkilerini doğrudan destekler.
Örnek: Bir Note birden fazla Category'ye ait olabilir ve bir Category birden fazla Note'a ait olabilir.
entity.HasMany(x => x.Categories)
.WithMany(c => c.Notes);
5. One-to-Many ve DeleteBehavior (Birden Bire Silme Davranışı)
Açıklama: OnDelete(DeleteBehavior) kullanılarak, ilişkili verilerin silme davranışı belirlenebilir. Bu, ilişkili verilerin nasıl silineceğini kontrol eder. Bu many-to-one veya one-to-many ilişkilerinde kullanılabilir.
Örnek: Bir User silindiğinde, ona ait Notes'un nasıl silineceği:
entity.HasOne(x => x.User)
.WithMany(u => u.Notes)
.OnDelete(DeleteBehavior.Cascade); // User silindiğinde Notes silinir
6. Many-to-One ve DeleteBehavior (Çoktan Bire Silme Davranışı)
Açıklama: Burada, birden fazla entity'nin bağlı olduğu ana entity'nin silinmesiyle ilişkili davranışları belirleyebilirsiniz. Yine DeleteBehavior burada geçerli olur.
Örnek: Bir Note silindiğinde User'ın silinip silinmeyeceği:
entity.HasOne(x => x.User)
.WithMany(u => u.Notes)
.OnDelete(DeleteBehavior.Restrict); // Note silindiğinde User silinmez
7. Self-Referencing (Kendi Kendine İlişki)
Açıklama: Bir entity kendi türüyle ilişki kurabilir. Bu tür ilişki, bir entity'nin kendi sınıfı içinde kendisine bağlı olmasını sağlar (örneğin, hiyerarşik yapılar, ağaç yapıları).
Örnek: Bir Employee'nin bir Manager'ı olabilir.
entity.HasOne(x => x.Manager)
.WithMany(m => m.Employees)
.HasForeignKey(x => x.ManagerId);
8. Shared Primary Key (Paylaşılan Birincil Anahtar)
Açıklama: İki entity'nin aynı birincil anahtarı paylaştığı bir ilişki türüdür. Bu türde, iki entity'nin birincil anahtarı aynıdır, genellikle bir entity'nin diğerine "owned" olması durumunda kullanılır.
Örnek: Bir User ve UserProfile aynı birincil anahtarı paylaşabilir.
entity.HasOne(x => x.Profile)
.WithOne(p => p.User)
.HasForeignKey<UserProfile>(p => p.UserId)
.IsRequired();
9. Many-to-Many (Geçici İlişki Tablosu ile)
Açıklama: Eğer EF Core 5'ten önce çalışıyorsanız veya özel bir yapı oluşturuyorsanız, many-to-many ilişkisini elle oluşturmanız gerekebilir. Bu durumda, ilişkiler için özel bir junction table (bağlantı tablosu) kullanılır.
Örnek: Bir Student birden fazla Course'a katılabilir ve her Course'a birden fazla Student kayıtlı olabilir. Bir ara tablosu (junction table) kullanılır.
entity.HasMany(x => x.Courses)
.WithMany(c => c.Students)
.UsingEntity<Enrollment>(
j => j.HasOne(e => e.Course).WithMany(),
j => j.HasOne(e => e.Student).WithMany()
);
