EF Core'da model, entity sınıflarınızın veritabanındaki tablolara, kolonlara ve ilişkilere nasıl eşlendiğini anlatan meta veridir. Entity Framework Core bu modeli önce konvansiyonlar ile, yani yaygın kalıpları tanıyan hazır kurallarla kendiliğinden kurar. Siz de onu iki yolla özelleştirirsiniz: sınıflara ve property'lere koyduğunuz data annotations (eşleme attribute'ları) ve OnModelCreating içinde ModelBuilder üzerinden çağırdığınız Fluent API. İkisi de konvansiyonun verdiği kararı değiştirir; ikisi çakıştığında Fluent API kazanır.
Bu yazı modelin genel yapılandırmasını anlatıyor: hangi tiplerin modele girdiği, tablo ve şema adları, view ve table-valued function eşlemesi, anahtarsız (keyless) entity tipleri, parametreli constructor kullanan entity'ler ve aynı ayarı birçok tipe birden uygulayan toplu yapılandırma. Tek tek kolon ayarları için property ve kolon yapılandırma, anahtarlar için primary key yazısı, index'ler için index ve unique index yazısı ayrı ayrı hazırlandı. Serinin tamamını EF Core öğrenme rehberi sayfasında bulabilirsiniz.
Model nasıl oluşur?

Modelin kurulumu konvansiyonlarla başlar. Belgelerdeki örneklerle anlatırsak: Id veya BlogId adlı bir property birincil anahtar sayılır, DbSet<Blog> Blogs property'si tablo adını Blogs yapar, getter ve setter'ı olan her public property bir kolona eşlenir, navigation property'lerden ilişkiler çıkarılır. Bu kuralların tamamı IConvention arayüzünü uygulayan sınıflar olarak EF Core içinde bulunur; üçüncü taraf sağlayıcıların ve eklentilerin getirdiği konvansiyonlar bu listeye dahil değildir.
Konvansiyonların ardından sizin açık yapılandırmanız devreye girer. Belgelerin vurguladığı bir ayrıntı da şudur: yapılandırmanın büyük kısmı hangi veri deposunu hedeflediğinizden bağımsızdır, ama veritabanı sağlayıcıları kendilerine özgü ek ayarlar sunabilir ve desteklemedikleri ya da anlamsız buldukları ayarları yok sayabilir. Örneğin belgelere göre SQLite şemaları desteklemez. Sağlayıcıya özgü ayarlar için veritabanı sağlayıcıları yazısına bakabilirsiniz.
| Yöntem | Nerede yazılır | Öncelik | Not |
|---|---|---|---|
| Konvansiyon | EF Core içinde, varsayılan | En düşük | Kaldırılabilir, değiştirilebilir, yenisi eklenebilir |
| Data annotations | Entity sınıfı ve property üzerinde | Orta | Konvansiyonu ezer, Fluent API tarafından ezilir |
| Fluent API | OnModelCreating | En yüksek | Sınıfa dokunmadan yapılandırma; çakışmada son çağrı kazanır |
| Pre-convention | ConfigureConventions | Açık yapılandırma gibi | Bir CLR tipinin tüm property'lerine uygulanır |
Fluent API ile yapılandırma
Kendi DbContext sınıfınızda OnModelCreating metodunu override eder ve gelen ModelBuilder ile ayarları yazarsınız. Belgeler bunu en güçlü yöntem olarak tanımlar, çünkü entity sınıflarını değiştirmeden her şeyi ayarlayabilirsiniz. Çağrılar yazıldığı sırayla uygulanır; aynı ayarı iki kez farklı değerle verirseniz sonraki çağrı öncekinin yerine geçer. Aşağıdaki örnek, Url property'sini zorunlu yapar; zorunlu property'ler ilişkisel veritabanında boş geçilemeyen (NOT NULL) kolon olarak oluşturulur.
using Microsoft.EntityFrameworkCore;
internal class MyContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.Property(b => b.Url)
.IsRequired();
}
}
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
}Yapılandırmayı IEntityTypeConfiguration ile gruplamak
Model büyüdükçe OnModelCreating yüzlerce satıra uzayabilir. Bir entity'nin bütün ayarlarını IEntityTypeConfiguration<TEntity> arayüzünü uygulayan ayrı bir sınıfa taşıyabilirsiniz. Configure metodu bir EntityTypeBuilder<Blog> alır ve içine yazdığınız çağrılar OnModelCreating içindekilerle aynıdır. Sonra bu sınıfı OnModelCreating içinden çağırırsınız:
public class BlogEntityTypeConfiguration : IEntityTypeConfiguration<Blog>
{
public void Configure(EntityTypeBuilder<Blog> builder)
{
builder
.Property(b => b.Url)
.IsRequired();
}
}
// MyContext icinde:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
new BlogEntityTypeConfiguration().Configure(modelBuilder.Entity<Blog>());
// veya bir assembly'deki tum yapilandirma siniflarini birden uygulamak icin:
modelBuilder.ApplyConfigurationsFromAssembly(typeof(BlogEntityTypeConfiguration).Assembly);
}ApplyConfigurationsFromAssembly, verilen assembly içinde IEntityTypeConfiguration uygulayan tüm tipleri bulup uygular. Pratik bir yöntemdir ama belgelerde iki önemli kısıt yazar. Birincisi, yapılandırmaların hangi sırayla uygulanacağı tanımlı değildir; bu yüzden yalnızca sıranın önemli olmadığı durumlarda kullanılmalıdır. İkincisi, yalnızca parametresiz constructor'ı olan yapılandırma sınıfları oluşturulur (constructor public olmak zorunda değildir). Parametre isteyen sınıflar atlanır ve SkippedEntityTypeConfigurationWarning uyarısı loglanır. Böyle bir sınıfı kendiniz oluşturup ModelBuilder.ApplyConfiguration metoduna vermeniz gerekir.
EntityTypeConfigurationAttribute ile bağlamak
Configure çağrısını elle yazmak yerine entity sınıfına [EntityTypeConfiguration(typeof(BookConfiguration))] attribute'unu koyabilirsiniz. EF Core, Book tipi modele her girdiğinde belirtilen yapılandırma sınıfını kullanır. Dikkat edilecek nokta: bu attribute'lar assembly taranarak kendiliğinden bulunmaz. Entity tipinin önce normal yollardan biriyle modele eklenmesi gerekir; bir DbSet<Book> property'si ya da modelBuilder.Entity<Book>() çağrısı bunun için yeterlidir.
[EntityTypeConfiguration(typeof(BookConfiguration))]
public class Book
{
public int Id { get; set; }
public string Title { get; set; }
public string Isbn { get; set; }
}
public class BooksContext : DbContext
{
public DbSet<Book> Books { get; set; }
}Data annotations ile yapılandırma
İkinci yol, eşlemeyi doğrudan sınıfın üzerine attribute olarak yazmaktır. Data annotations konvansiyonları ezer, ama aynı ayarı Fluent API ile de verirseniz Fluent API kazanır. Aşağıdaki örnekte [Table] tablo adını, [Required] ise Url kolonunun zorunlu olduğunu belirtir:
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;
internal class MyContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
}
[Table("Blogs")]
public class Blog
{
public int BlogId { get; set; }
[Required]
public string Url { get; set; }
}Hangisini seçeceğiniz biraz da ekip tercihidir. Attribute'lar eşlemeyi sınıfın yanında, göz önünde tutar; Fluent API ise domain sınıflarınızı EF Core'a özgü ayrıntılardan arındırır ve daha fazla ayarı kapsar. Belgelerde bazı ayarlar, örneğin index filtresi, check constraint, varsayılan kolon değeri veya view eşlemesi, yalnızca Fluent API ile gösterilir. Birçok ekip ikisini birlikte kullanır: basit ve görünür olması istenen ayarlar (zorunluluk, uzunluk) attribute ile, geri kalanı Fluent API ile yazılır. Önemli olan, aynı ayarı iki yerde farklı değerlerle vermemektir; aksi hâlde hangi değerin geçerli olduğunu bulmak için öncelik kuralını hatırlamanız gerekir.
Modele hangi tipler girer?
Bir tip üç yoldan modele girer ve bu durumda ona entity deriz. Context üzerinde bir DbSet property'si ile açılan tipler, OnModelCreating içinde Entity<T>() ile belirtilen tipler ve bunlardan navigation property'ler üzerinden özyinelemeli olarak keşfedilen tipler. Aşağıdaki örnekte üç sınıf da modele girer: Blog DbSet sayesinde, Post Blog.Posts navigation'ı sayesinde, AuditEntry ise OnModelCreating içinde açıkça belirtildiği için.
internal class MyContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<AuditEntry>();
}
}
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
public List<Post> Posts { get; set; }
}
public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public Blog Blog { get; set; }
}
public class AuditEntry
{
public int AuditEntryId { get; set; }
public string Username { get; set; }
public string Action { get; set; }
}Navigation üzerinden keşif bazen istemediğiniz bir tipi modele sokar. Bir tipi tamamen dışarıda tutmak için sınıfa [NotMapped] koyabilir ya da Fluent API ile modelBuilder.Ignore<BlogMetadata>() çağırabilirsiniz. İlişkilerin nasıl keşfedildiğini ilişkiler ve navigation yazısında ayrıntılı anlattık.
[NotMapped]
public class BlogMetadata
{
public DateTime LoadedFromDatabase { get; set; }
}
// veya Fluent API ile:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Ignore<BlogMetadata>();
}Tipi modelde tutup migration'dan çıkarmak
Aynı entity tipini birden fazla DbContext'te kullanmak, özellikle her bounded context için ayrı bir DbContext tanımlayan projelerde yaygındır. Bu durumda tablonun yalnızca bir context'in migration'ları tarafından yönetilmesini istersiniz. ToTable içinde ExcludeFromMigrations() çağrısı, tipi modelde ve sorgularda kullanılabilir bırakır ama migration'ların o tabloyu oluşturmasını engeller. Tabloyu ileride yeniden migration ile yönetmek isterseniz, dışlamanın kaldırıldığı yeni bir migration oluşturursunuz; sonraki migration tabloya yapılan değişiklikleri içerir. Günlük migration akışı için migration rehberine bakabilirsiniz.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<IdentityUser>()
.ToTable("AspNetUsers", t => t.ExcludeFromMigrations());
}Tablo adı, şema ve tablo yorumu
Konvansiyon gereği her entity, kendisini açan DbSet property'siyle aynı adı taşıyan tabloya eşlenir. Entity için DbSet yoksa sınıf adı kullanılır. Tablolar da veritabanının varsayılan şemasında oluşturulur; SQL Server için bu dbo şemasıdır. Her ikisini de attribute ya da Fluent API ile değiştirebilirsiniz. Şemayı her tabloya ayrı ayrı yazmak yerine HasDefaultSchema ile model düzeyinde de verebilirsiniz; belgeler bunun sequence gibi diğer veritabanı nesnelerini de etkilediğini hatırlatır (sequence'lar için değer üretimi yazısına bakın).
// Data annotations
[Table("blogs", Schema = "blogging")]
[Comment("Blogs managed on the website")]
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
}
// Fluent API karsiligi
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.ToTable("blogs", schema: "blogging");
modelBuilder.Entity<Blog>().ToTable(
tableBuilder => tableBuilder.HasComment("Blogs managed on the website"));
// Tum model icin varsayilan sema:
// modelBuilder.HasDefaultSchema("blogging");
}[Comment] veya HasComment ile verdiğiniz metin veritabanındaki tabloya yorum olarak yazılır; şemayı veritabanı içinde belgelemek için kullanışlıdır. Kolon düzeyindeki yorumlar property ve kolon yapılandırma yazısında anlatılıyor.
View, table-valued function ve trigger eşlemesi
Bir entity'yi tablo yerine veritabanı view'ına eşleyebilirsiniz: modelBuilder.Entity<Blog>().ToView("blogsView", schema: "blogging"). Burada kritik nokta, EF Core'un view'ın veritabanında zaten var olduğunu varsaymasıdır; migration view'ı sizin yerinize oluşturmaz. View'ı migration içinde ham SQL ile oluşturmayı migration özelleştirme yazısında anlattık. View eşlemesi varsayılan tablo eşlemesini kaldırır, ama entity'yi ayrıca açıkça bir tabloya da eşleyebilirsiniz. Bu durumda sorgular view'dan okunur, güncellemeler tabloya yazılır.
Entity'yi parametresiz bir table-valued function'a (TVF) da eşlemek mümkündür. Belgelerdeki örnekte birden fazla yazısı olan blogları ve yazı sayılarını döndüren bir fonksiyon, anahtarsız bir entity'ye eşlenir. Fonksiyonun döndürdüğü kolonlar property adlarıyla eşleşir; adlar farklıysa normal tabloda olduğu gibi HasColumnName kullanırsınız. Fonksiyonun parametresiz olması şarttır.
public class BlogWithMultiplePosts
{
public string Url { get; set; }
public int PostCount { get; set; }
}
modelBuilder.Entity<BlogWithMultiplePosts>().HasNoKey().ToFunction("BlogsWithMultiplePosts");
var query = from b in context.Set<BlogWithMultiplePosts>()
where b.PostCount > 3
select new { b.Url, b.PostCount };Belgelerin bu sorgu için gösterdiği SQL çıktısı şöyledir:
SELECT [b].[Url], [b].[PostCount]
FROM [dbo].[BlogsWithMultiplePosts]() AS [b]
WHERE [b].[PostCount] > 3Tablolardaki trigger'lar için de benzer bir kural var: ToTable(tb => tb.HasTrigger("TRG_InsertUpdateBlog")) çağrısı trigger'ı yalnızca EF modeline kaydeder. Migration'lar trigger'ı veritabanında oluşturmaz; trigger DDL'ini migrationBuilder.Sql ile bir migration'a sizin eklemeniz gerekir.
Keyless entity types: anahtarsız entity tipleri

Normal entity'lerin bir anahtarı vardır. Ama bazı veriler anahtar içermez: birincil anahtarı olmayan bir view, PK tanımlanmamış eski bir tablo ya da ham SQL sorgusunun döndürdüğü özet satırlar. Bunlar için keyless entity type kullanılır. Belgeler, bu özelliğin önce "query types" adıyla eklendiğini, sonra yeniden adlandırıldığını not eder. Tanımlamak için sınıfa [Keyless] koyarsınız ya da Fluent API ile HasNoKey() çağırırsınız. Keyless entity'ler konvansiyonla hiçbir zaman keşfedilmez; bu iki yoldan biriyle açıkça işaretlenmeleri gerekir.
Belgelere göre keyless entity tiplerinin normal entity'lerden farkları şunlardır:
- Anahtar tanımlanamaz.
- DbContext tarafından değişiklikleri hiçbir zaman takip edilmez; bu yüzden veritabanına eklenmez, güncellenmez, silinmez.
- İlişkilerde hiçbir zaman principal (asıl) taraf olamaz; owned entity'lere navigation içeremez; yalnızca normal entity'lere işaret eden referans navigation'ları olabilir. Normal entity'ler de keyless tiplere navigation içeremez.
- Modelde tanımlı bir sorguya (defining query) eşlenebilir.
- Kalıtım hiyerarşisi kurulabilir ama yalnızca TPH olarak eşlenir; table splitting ve entity splitting kullanılamaz.
Kullanım alanları da belgelerde sayılır: ham SQL sorgularının dönüş tipi olmak, birincil anahtarı olmayan view'lara ve tablolara eşlemek, modelde tanımlı sorgulara eşlemek. Keyless bir tipi ToTable ya da ToView ile bir veritabanı nesnesine eşlediğinizde EF Core o nesneyi her durumda salt okunur bir sorgu kaynağı, yani view gibi görür; nesne gerçekte bir tablo olsa bile. Normal entity'lerde ise ToTable ile verilen nesne hem sorgulanabilir hem güncellenebilir kabul edilir; hatta güncellenebilir bir view'ın adını ToTable ile vermek de çalışır.
public class BlogPostsCount
{
public string BlogName { get; set; }
public int PostCount { get; set; }
}
// DbContext icinde
public DbSet<BlogPostsCount> BlogPostCounts { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder
.Entity<BlogPostsCount>(
eb =>
{
eb.HasNoKey();
eb.ToView("View_BlogPostCounts");
eb.Property(v => v.BlogName).HasColumnName("Name");
});
}
// Sorgu: view normal bir DbSet gibi okunur
var postCounts = await db.BlogPostCounts.ToListAsync();Parametreli constructor kullanan entity'ler

EF Core bir sorgu sonucundan entity oluştururken varsayılan olarak önce parametresiz constructor'ı çağırır, sonra her property'ye veritabanındaki değeri atar. Ama sınıfta parametre adları ve tipleri eşlenmiş property'lerle uyuşan bir constructor bulursa onu çağırır ve o property'leri ayrıca atamaz. Belgelerin bu konudaki kuralları:
- Her property'nin constructor parametresi olması gerekmez; parametresi olmayanlar constructor'dan sonra normal yoldan atanır.
- Parametre tipi ve adı property ile uyuşmalıdır; tek esneklik, property PascalCase iken parametrenin camelCase olabilmesidir.
- Navigation property'ler (
Blog,Postsgibi) constructor ile atanamaz. - Constructor'ın erişim düzeyi serbesttir; ancak lazy loading proxy'leri, türeyen proxy sınıfının erişebileceği bir constructor ister, bu da genellikle public veya protected demektir (lazy loading yazısı).
Constructor ile değer atamaya başladığınızda bazı property'leri salt okunur yapmak istersiniz. Burada iki tuzak var: setter'ı olmayan property'ler konvansiyonla eşlenmez ve veritabanının ürettiği anahtar değerleri, ekleme sırasında anahtar üretecinin değeri yazabilmesi için okunur-yazılır bir anahtar property'si gerektirir. En kolay çözüm private setter kullanmaktır; EF Core private setter'lı bir property'yi okunur-yazılır sayar, böylece eşleme ve anahtar üretimi eskisi gibi çalışır.
public class Blog
{
public Blog(int id, string name, string author)
{
Id = id;
Name = name;
Author = author;
}
public int Id { get; private set; }
public string Name { get; private set; }
public string Author { get; private set; }
public ICollection<Post> Posts { get; } = new List<Post>();
}Alternatif olarak property'leri gerçekten salt okunur bırakıp OnModelCreating içinde b.Property(e => e.Name) gibi açık eşleme ekleyebilir, anahtarı ise yalnızca bir alanda tutabilirsiniz: b.HasKey("_id"). Bu alan, veritabanının ürettiği anahtarlar kullanılabilsin diye readonly olmamalıdır. Derleyici alanın hiç kullanılmadığını söyleyen 169 numaralı uyarıyı verebilir; belgelere göre bu uyarı yok sayılabilir, çünkü alanı EF Core dil dışı bir yolla kullanır.
EF Core constructor'a bazı servisleri de enjekte edebilir: geçerli DbContext (kendi türettiğiniz tip olarak da), ILazyLoader, Action<object, string> tipinde bir lazy loading delegesi ve entity tipinin meta verisi olan IEntityType. Şimdilik yalnızca EF Core'un bildiği servisler enjekte edilebilir, uygulama servisleri edilemez. Belgeler DbContext'i bu şekilde entity'ye enjekte etmenin çoğu zaman anti-pattern sayıldığını, çünkü entity'yi doğrudan EF Core'a bağladığını açıkça uyarır.
Toplu yapılandırma: ConfigureConventions ve konvansiyonlar
Aynı ayarı onlarca property'ye tek tek yazmak hem uzun hem hataya açıktır. EF Core bunun için birkaç yol sunar. En kolayı pre-convention yapılandırmadır: DbContext'te ConfigureConventions metodunu override eder, bir CLR tipi için ayarı bir kez verirsiniz. Ayar, o tipteki her property modele eklendiği anda uygulanır. Aşağıdaki örnek tüm string property'leri Unicode olmayan ve en fazla 1024 karakterlik kolonlar yapar:
protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
{
configurationBuilder
.Properties<string>()
.AreUnicode(false)
.HaveMaxLength(1024);
// Bir tipi hem entity hem property olarak kesfedilmekten cikarmak:
configurationBuilder
.IgnoreAny(typeof(IList<>));
}ConfigureConventions içinde verdiğiniz tip bir arayüz, bir taban tip ya da generic tip tanımı da olabilir. Eşleşen tüm ayarlar en genelden en özele doğru şu sırayla uygulanır: arayüz, taban tip, generic tip tanımı, nullable olmayan değer tipi, tam tip. Aynı mekanizma değer dönüştürücüleri için de kullanılır; örneğin kendi para tipiniz için HaveConversion ile bir converter bağlamayı value conversion yazısında anlattık. Belgeler bu yöntemin sınırlarını da sayar: pek çok ayar bu yolla verilemez, seçim yalnızca CLR tipine göre yapılır ve bir çakışma çıkarsa hata yığınında ConfigureConventions görünmez, bu da nedeni bulmayı zorlaştırır.
İkinci yol, OnModelCreating içinde modelBuilder.Model.GetEntityTypes() ile modeldeki tipleri dolaşıp meta veri API'si ile ayar yapmaktır. Bu yöntemin dezavantajları belgelerde açıkça yazar: her değişiklik açıkça yapılmalıdır, konvansiyonlar her değişiklikten sonra yeniden çalışır ve döngüden sonra modele eklenen tipler ayarı almaz. Sonuncusu genellikle bu kodu OnModelCreating'in sonuna koyarak önlenir.
Üçüncü yol konvansiyonların kendisidir. Var olan bir konvansiyonu kaldırabilir, değiştirebilir ya da yenisini ekleyebilirsiniz. Belgelerdeki örnek, foreign key kolonlarına otomatik index açan ForeignKeyIndexConvention'ı kaldırır; index'lerin bakım maliyeti olduğu için her FK'ye index istemeyebilirsiniz (ayrıntı için index yazısı).
protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
{
configurationBuilder.Conventions.Remove(typeof(ForeignKeyIndexConvention));
}Kendi yazdığınız konvansiyonlar, örneğin IModelFinalizingConvention uygulayıp her string property'ye varsayılan uzunluk veren bir sınıf, convention builder üzerinden çalıştığında data annotation veya açık yapılandırmayla verilmiş değerleri ezmez. EF Core her ayarın kaynağını (Explicit, DataAnnotation, Convention) takip ettiği için bu ayrım mümkündür. Hangi yolu seçeceğinizi belgelerdeki şu özet belirler:
| Yaklaşım | Ne zaman |
|---|---|
| Meta veri API (OnModelCreating döngüsü) | Ayar belli bir anda uygulanmalı ve sonraki değişikliklere tepki vermemeli; model oluşturma hızı çok önemli |
| Pre-convention (ConfigureConventions) | Koşul yalnızca tipe bağlı; ayar konvansiyon ve attribute'ları ezmeli |
| Finalizing convention | Koşul karmaşık; ayar data annotations ile verileni ezmemeli |
| Interactive convention | Konvansiyonlar birbirine bağlı ya da mantık birden çok context arasında paylaşılıyor |
Modeli görmek: debug view
Konvansiyonların, attribute'ların ve Fluent API'nin sonunda ortaya ne çıktığını görmenin en kısa yolu modelin debug görünümüdür. IDE'nin debugger'ında açabileceğiniz gibi koddan konsola da yazdırabilirsiniz. Kısa biçim entity'leri, property'leri, anahtarları, foreign key'leri ve index'leri listeler; uzun biçim ayrıca tüm annotation'ları içerir, bu da ilişkisel veya sağlayıcıya özgü meta veriyi incelerken işe yarar.
Console.WriteLine(context.Model.ToDebugString());
// Uzun bicim (tum annotation'lar dahil):
Console.WriteLine(context.Model.ToDebugString(MetadataDebugStringOptions.LongDefault));Change tracker'ın debug görünümü ise ayrı bir konudur; onu change tracker yazısında anlattık.
Sık yapılan hatalar
- ToView veya HasTrigger'ın nesneyi oluşturmasını beklemek. İkisi de yalnızca modele bilgi verir; view ve trigger'ı migration içinde ham SQL ile siz oluşturursunuz.
- Sıranın önemli olduğu yapılandırmalarda ApplyConfigurationsFromAssembly kullanmak. Uygulama sırası tanımsızdır; parametreli constructor'ı olan yapılandırma sınıfları da sessizce atlanır, yalnızca uyarı loglanır.
- Setter'ı olmayan property'nin eşlenmesini beklemek. Konvansiyon onu eşlemez; private setter kullanın ya da property'yi Fluent API ile açıkça eşleyin.
- Keyless entity'yi SaveChanges ile kaydetmeye çalışmak. Bu tipler hiç takip edilmez, bu yüzden ekleme, güncelleme ve silme yapılamaz.
- ConfigureConventions ayarının yalnızca bazı property'lere uygulanacağını sanmak. Ayar o tipteki her property'yi etkiler, foreign key'ler dahil.
- EntityTypeConfigurationAttribute'un kendiliğinden bulunacağını düşünmek. Entity'nin DbSet ya da Entity<T>() ile modele eklenmesi gerekir.
Sık Sorulan Sorular
Fluent API mi, data annotations mı kullanmalıyım?
İkisi de geçerlidir ve birlikte kullanılabilir. Fluent API daha fazla ayarı kapsar, en yüksek önceliğe sahiptir ve sınıflarınızı EF Core'a bağımlı attribute'lardan uzak tutar. Data annotations ise basit kuralları sınıfın yanında görünür kılar. Aynı ayarı iki yerde farklı değerle vermemeye dikkat edin; çakışmada Fluent API kazanır.
OnModelCreating içinde çağrıların sırası önemli mi?
Evet. Çağrılar yazıldığı sırayla uygulanır ve aynı ayar için sonraki çağrı öncekinin yerine geçer. Meta veri API'si ile yapılan döngülerde de sıra önemlidir; döngüden sonra eklenen tipler ayarı almaz.
Aynı entity tipini iki farklı DbContext içinde kullanabilir miyim?
Evet, bounded context yaklaşımında bu yaygındır. Tablonun yalnızca bir context'in migration'larıyla yönetilmesi için diğer context'te ToTable(..., t => t.ExcludeFromMigrations()) kullanırsınız; tip modelde kalır ve normal şekilde sorgulanır.
Keyless entity ile ham SQL sorgusu arasındaki ilişki nedir?
Keyless entity'lerin başlıca kullanım alanlarından biri, ham SQL sorgularının dönüş tipi olmaktır. Sorgunun döndürdüğü kolonlar property'lere eşlenir, sonuç salt okunur gelir. Ham SQL API'lerinin ayrıntıları ve parametre güvenliği için FromSql yazısına bakın.
Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.
İlgili içerikler: EF Core öğrenme rehberi · Property ve kolon yapılandırma · Primary key ve composite key · Index ve unique index · Value conversion · Kalıtım: TPH, TPT, TPC · C# nedir?