EF Core'da data seeding, veritabanını bir başlangıç veri kümesiyle doldurma işidir. Bugünkü belgelere göre önerilen yol, EF 9 ile gelen UseSeeding ve UseAsyncSeeding metotlarıdır: DbContext seçeneklerinde tanımlanan bu delegeler EnsureCreated, Migrate ve dotnet ef database update sırasında çalışır. Eski adıyla "data seeding" olarak bilinen HasData ise artık model managed data (modelin yönettiği veri) olarak adlandırılıyor ve yalnızca migration'larla yönetilen sabit veriler için uygun. Entity Framework Core belgeleri bu ikisine ek olarak uygulama içinden özel başlatma kodunu ve migration'ı elle özelleştirmeyi de anlatır.
Bu yazıda dört yöntemi, her birinin hangi dağıtım biçiminde çalıştığını, HasData'nın sınırlarını ve EF 9'dan beri bilinen "bekleyen model değişikliği" tuzağını ele alıyoruz. Migration'ın günlük akışı migration rehberinde, canlı ortama uygulanması production migration yazısında anlatılıyor. Serinin tamamı EF Core öğrenme rehberi sayfasında.
Dört yöntem bir bakışta

| Yöntem | Nerede tanımlanır | Ne zaman çalışır | Uygun olduğu veri |
|---|---|---|---|
| UseSeeding / UseAsyncSeeding | DbContext seçenekleri | EnsureCreated, Migrate, database update, bundle | Genel amaçlı başlangıç verisi (önerilen) |
| Özel başlatma kodu | Uygulama kodu | Siz çağırdığınızda | Örneğin iki context'in tek transaction'da kullanılması |
| HasData (model managed data) | OnModelCreating | Migration uygulanırken | Sabit, migration dışında değişmeyen veri |
| Elle migration özelleştirme | Migration dosyası | Migration uygulanırken | Anahtarları ve değerleri yazım anında sabit veri |
UseSeeding ve UseAsyncSeeding
EF 9 ile gelen bu iki metot, özel başlatma kodu yazma deneyimini iyileştirmek için tasarlandı. Tüm seed kodunun yaşayacağı tek ve açık bir yer sağlarlar. Daha önemlisi, bu metotların içindeki kod migration kilitleme mekanizması tarafından korunur; böylece eşzamanlılık sorunları önlenir. Delegeler, model değişikliği olmasa ve hiçbir migration uygulanmasa bile EnsureCreated, Migrate ve dotnet ef database update sırasında çağrılır. Bu yüzden kodunuz her çalıştırmada güvenle tekrarlanabilir olmalıdır: veriyi eklemeden önce zaten var olup olmadığını kontrol edin. Belgelerdeki örnek tam olarak bunu yapar:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder
.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=EFDataSeeding;Trusted_Connection=True;ConnectRetryCount=0")
.UseSeeding((context, _) =>
{
var testBlog = context.Set<Blog>().FirstOrDefault(b => b.Url == "http://test.com");
if (testBlog == null)
{
context.Set<Blog>().Add(new Blog { Url = "http://test.com" });
context.SaveChanges();
}
})
.UseAsyncSeeding(async (context, _, cancellationToken) =>
{
var testBlog = await context.Set<Blog>().FirstOrDefaultAsync(b => b.Url == "http://test.com", cancellationToken);
if (testBlog == null)
{
context.Set<Blog>().Add(new Blog { Url = "http://test.com" });
await context.SaveChangesAsync(cancellationToken);
}
});Örnekteki bağlantı dizesi yerel geliştirme içindir; gerçek ortamda bağlantı bilgisini kaynak koddan uzak tutmayı connection string yazısında anlattık. Seçeneklerin OnConfiguring yerine AddDbContext ile verilmesi de mümkündür; DbContext yapılandırma yolları DbContext yazısında.
Hangi dağıtım biçimi seed kodunu çalıştırır?
UseSeeding ve UseAsyncSeeding yalnızca EF Core bir veritabanı başlatma ya da migration işlemi yaptığında çalışır. Dağıtım yönteminizi buna göre seçmeniz gerekir. Belgelerdeki eşleme şöyledir:
| İşlem | Çalışan delege |
|---|---|
| EnsureCreated veya Migrate | UseSeeding |
| EnsureCreatedAsync veya MigrateAsync | UseAsyncSeeding |
| dotnet ef database update veya Update-Database | UseSeeding |
| Migration bundle | UseSeeding |
| Harici bir SQL aracıyla çalıştırılan SQL script | Hiçbiri |
Son satır canlı ortamlar için kritik bir ayrıntıdır. Migration'larınızı bir DBA'nın onayından geçen SQL script'leriyle uyguluyorsanız seed delegeleriniz hiç çalışmaz, çünkü script EF Core'un dışında yürütülür. Belgelerin önerisi şöyledir: seed kodunun çalışması gereken otomatik dağıtımlarda migration bundle ya da ayrı bir başlatma süreci kullanın; SQL'in gözden geçirilmesi veya DBA tarafından çalıştırılması gerekiyorsa SQL script kullanın ve seed verisini migration işlemleri olarak ifade edin. Uygulama migration belgesindeki strateji tablosu da aynı şeyi söyler: SQL script migration kilitlemesini kullanmaz ve seed delegelerini çalıştırmaz; bundle, komut satırı araçları ve çalışma zamanında migration ise ikisini de yapar.
Kilitleme mekanizmasının kendisi de bilinmeye değer. EF Core 9'dan beri Migrate ve MigrateAsync, herhangi bir migration'ı uygulamadan önce veritabanı genelinde bir kilit alır. Kilit, seed kodu dahil migration yürütmesi boyunca tutulur ve işlem bitince bırakılır. Bu, birden çok uygulama örneğinin aynı anda migration çalıştırmasından doğabilecek bozulmalara karşı korur. Belgeler kilitleme mekanizmasının sağlayıcıdan sağlayıcıya önemli ölçüde değiştiğini de uyarır; örneğin SQLite sağlayıcısı, süreç beklenmedik biçimde sonlanırsa sahipsiz kalabilen bir kilit tablosu kullanır.
Belgelerin eklediği iki ayrıntı daha var. Seed kodu migration geri alındıktan (downgrade) sonra da çalışır. Uygulamanız, seed kodunun kullandığı tabloların henüz bulunmadığı bir migration'a geri dönmeyi destekliyorsa, sorgulamadan önce gerekli şemanın var olduğunu kontrol edin; bu özellikle 0 hedeflenerek tüm migration'ların geri alındığı durumda önemlidir. Bir de yetki konusu: belgeler dağıtım için şemayı değiştirme yetkisi olan ayrı bir kimlik kullanmayı, uygulamanın çalışma zamanındaki kimliğinin ise normalde yalnızca veriyi okuyup yazmaya yetecek izinlere sahip olmasını önerir.
Özel başlatma kodu
Bir diğer yol, ana uygulama mantığı başlamadan önce seed verisini doğrudan kodla ekleyip SaveChangesAsync çağırmaktır. Belgeler bu iş için de UseSeeding/UseAsyncSeeding'i önerir, ama bunun her zaman iyi bir çözüm olmadığını kabul eder. Örnek senaryo: seed işleminin iki farklı context'i tek bir transaction içinde kullanması gerektiğinde.
using (var context = new DataSeedingContext())
{
await context.Database.EnsureCreatedAsync();
var testBlog = await context.Blogs.FirstOrDefaultAsync(b => b.Url == "http://test.com");
if (testBlog == null)
{
context.Blogs.Add(new Blog { Url = "http://test.com" });
await context.SaveChangesAsync();
}
}Bu örnek, yalnızca EnsureCreatedAsync kullanan basit senaryolar içindir. Migration ile yönetilen bir ilişkisel veritabanında EnsureCreatedAsync çağırmamanız gerektiğini aşağıda tekrar hatırlatıyoruz.
HasData: model managed data

Veriyi model yapılandırmasının bir parçası olarak da tanımlayabilirsiniz. Bu durumda EF Core migration'ları, modeli yeni bir sürüme taşırken hangi ekleme, güncelleme ve silme işlemlerinin uygulanması gerektiğini kendisi hesaplar. OnModelCreating içinde HasData ile ülkeler gibi sabit bir listeyi tanımlarsınız. İlişkili entity'ler eklerken foreign key değerlerini açıkça vermeniz gerekir; aşağıdaki şehirlerin her biri, bulunduğu ülkeyi LocatedInId değeriyle gösterir.
modelBuilder.Entity<Country>(b =>
{
b.Property(x => x.Name).IsRequired();
b.HasData(
new Country { CountryId = 1, Name = "USA" },
new Country { CountryId = 2, Name = "Canada" },
new Country { CountryId = 3, Name = "Mexico" });
});
modelBuilder.Entity<City>().HasData(
new City { Id = 1, Name = "Seattle", LocatedInId = 1 },
new City { Id = 2, Name = "Vancouver", LocatedInId = 2 },
new City { Id = 3, Name = "Mexico City", LocatedInId = 3 },
new City { Id = 4, Name = "Puebla", LocatedInId = 3 });Many-to-many ilişkilerde join entity'nin açıkça yapılandırılması gerekir. Join entity'nin shadow state'te property'leri varsa (belgelerdeki LanguageCountry gibi), değerleri anonim bir sınıfla verebilirsiniz: je.HasData(new { LanguageId = 1, CountryId = 2 }). Join entity'nin UsingEntity ile yapılandırılması many-to-many yazısında anlatılıyor. Owned entity tipleri de benzer biçimde, OwnsOne(...).HasData(...) içinde anonim nesnelerle doldurulur.
modelBuilder.Entity<Language>().OwnsOne(p => p.Details).HasData(
new { LanguageId = 1, Phonetic = false, Tonal = false, PhonemesCount = 44 },
new { LanguageId = 2, Phonetic = false, Tonal = false, PhonemesCount = 36 },
new { LanguageId = 3, Phonetic = true, Tonal = false, PhonemesCount = 24 });Veriyi modele ekledikten sonra değişikliği veritabanına migration ile taşırsınız. Migration oluşturulurken HasData değişiklikleri InsertData, UpdateData ve DeleteData işlemlerine dönüştürülür. Belgelerin vurguladığı nokta şudur: Migrate çağrısı mevcut HasData yapılandırmasını ayrıca incelemez. Yani modeldeki veriyi değiştirdikten sonra yeni bir migration ekleyip dağıtmanız gerekir; yalnızca uygulamayı yeniden başlatmak veriyi güncellemez. Otomatik dağıtım için migration bundle, çalışmadan önce önizlenmesi veya değiştirilmesi gereken durumlarda SQL script önerilir.
EnsureCreatedAsync da managed data'yı içeren yeni bir veritabanı oluşturabilir; test veritabanları, in-memory sağlayıcı ya da ilişkisel olmayan veritabanları için bu işe yarar. Ancak veritabanı zaten varsa EnsureCreatedAsync ne şemayı ne de managed data'yı günceller. İlişkisel veritabanlarında migration kullanmayı planlıyorsanız EnsureCreatedAsync'i hiç çağırmamalısınız. Test tarafındaki kullanım için EF Core test yazma rehberine bakabilirsiniz.
HasData neden artık "seeding" sayılmıyor?
Belgeler, HasData ile veritabanını doldurmanın eskiden "data seeding" diye anıldığını, ama bu adın yanlış beklentiler yarattığını açıkça yazar: özelliğin birçok sınırı vardır ve yalnızca belirli veri türleri için uygundur. Bu yüzden adı "model managed data" olarak değiştirildi; genel amaçlı seeding için UseSeeding ve UseAsyncSeeding kullanılmalı. Sınırların kaynağı şudur: bu veri migration'larla yönetilir ve mevcut veriyi güncelleyen script'in veritabanına bağlanmadan üretilmesi gerekir. Bunun sonuçları:
- Birincil anahtar değeri, normalde veritabanı tarafından üretilse bile verilmelidir; migration'lar arasında veri değişikliklerini algılamak için kullanılır.
- Birincil anahtar herhangi bir şekilde değiştirilirse daha önce eklenen veri silinir.
Bu nedenle özellik en çok, migration dışında değişmesi beklenmeyen ve veritabanındaki başka hiçbir şeye bağlı olmayan statik veriler için yararlıdır; belgelerin örneği posta kodlarıdır. Senaryonuz aşağıdakilerden birini içeriyorsa belgeler UseSeeding/UseAsyncSeeding'i önerir:
- Test için geçici veri.
- Veritabanının durumuna bağlı veri.
- Büyük veri: seed verisi migration snapshot'larına yazılır, büyük veri hızla dev dosyalara ve performans düşüşüne yol açar.
- Anahtar değerlerinin veritabanında üretilmesi gereken veri; kimlik olarak alternate key kullanan entity'ler dahil.
- Bazı parola hash'leme işlemleri gibi, value conversion ile karşılanmayan özel dönüşüm gerektiren veri.
- ASP.NET Core Identity rol ve kullanıcılarının oluşturulması gibi dış API çağrısı gerektiren veri.
- Sabit ve belirlenimci olmayan veri; örneğin
DateTime.Nowile doldurulan değerler.
Bekleyen model değişikliği tuzağı
Son maddenin pratikte çok somut bir karşılığı var. EF Core 9'dan beri Migrate() veya MigrateAsync(), model son migration'a göre bekleyen değişiklikler içeriyorsa istisna fırlatır (uyarı olay kimliği RelationalEventId.PendingModelChangesWarning). EF 9 breaking changes belgesi bu durumun olası nedenlerinden birini açıkça sayar: model geliştirici tarafından değiştirilmemiştir ama belirlenimci olmayan biçimde kurulduğu için EF onu değişmiş görür. Bu, HasData'ya verilen nesnelerde new DateTime(), DateTime.Now, DateTime.UtcNow veya Guid.NewGuid() kullanıldığında yaygındır.
// Hatali: her model olusturmada farkli deger uretir
modelBuilder.Entity<Blog>().HasData(
new Blog { BlogId = 1, Url = "http://test.com", Created = DateTime.Now });
// Dogru: sabit, elle yazilmis deger
modelBuilder.Entity<Blog>().HasData(
new Blog { BlogId = 1, Url = "http://test.com", Created = new DateTime(2025, 1, 1) });Belgelerin önerdiği çözüm: yeni bir migration ekleyip içeriğini inceleyerek nedeni bulun, dinamik veriyi modelde sabit, elle yazılmış bir değerle değiştirin ve model düzeltildikten sonra migration'ı yeniden oluşturun. Dinamik veriyle seed gerçekten gerekiyorsa HasData yerine yeni seeding yöntemini kullanın. Dağıtımdan önce bu durumu yakalamak için CI hattınızda dotnet ef migrations has-pending-model-changes komutunu çalıştırabilirsiniz. Hatanın diğer nedenleri ve çözümleri pending model changes yazısında ayrıntılı olarak anlatılıyor.
dotnet ef migrations has-pending-model-changesMigration içinde elle veri işlemleri
Migration eklendiğinde HasData ile verilen değişiklikler InsertData(), UpdateData() ve DeleteData() çağrılarına dönüşür. HasData'nın bazı sınırlarını aşmanın bir yolu, bu çağrıları ya da özel işlemleri migration'a kendiniz eklemektir. Belgelere göre bu işlemler, değerler ve anahtarlar migration yazılırken sabitse uygundur; mevcut veritabanı durumunu sorgulamazlar.
migrationBuilder.InsertData(
table: "Countries",
columns: new[] { "CountryId", "Name" },
values: new object[,]
{
{ 1, "USA" },
{ 2, "Canada" },
{ 3, "Mexico" }
});Migration'a ham SQL ve özel işlemler eklemenin ayrıntıları migration özelleştirme yazısında. Elle eklenen veri işlemleri, SQL script ile dağıtım yapan ekipler için de uygundur, çünkü script'e migration'ın bir parçası olarak girer.
Hangi yöntemi seçmeli?
Belgelerin önerilerini bir karar sırasına çevirirsek: genel amaçlı başlangıç verisi, ortama göre değişen veri, veritabanının ürettiği anahtarlar ya da dış servis çağrısı gerekiyorsa UseSeeding ve UseAsyncSeeding kullanın ve dağıtımı bundle ya da EF araçlarıyla yapın. Ülke ve şehir listesi, sabit kategori kodları gibi küçük, statik ve migration dışında değişmeyen veriler için HasData uygundur; anahtarları siz verirsiniz ve değişiklikler migration ile taşınır. SQL script ile dağıtım yapıyorsanız seed verisini migration işlemleri olarak ifade edin, çünkü script seed delegelerini çalıştırmaz. Özel başlatma kodunu ise yalnızca diğer yolların yetmediği, örneğin iki context'in tek transaction'da kullanılması gereken durumlar için ve normal uygulama akışının dışında çalıştırın.
Sık yapılan hatalar

- Yalnızca UseAsyncSeeding tanımlamak. Araçlar ve bundle'lar senkron UseSeeding'i çağırır.
- SQL script ile dağıtıp seed kodunun çalışmasını beklemek. Harici araçla çalıştırılan script hiçbir seed delegesini çağırmaz.
- Seed kodunu her çalıştırmada tekrar eklemeye uygun yazmamak. Delegeler migration olmasa bile çağrılır; önce verinin varlığını kontrol edin.
- HasData içinde DateTime.Now veya Guid.NewGuid kullanmak. Model her seferinde farklı görünür ve Migrate bekleyen model değişikliği nedeniyle hata verir.
- HasData'yı değiştirip yeni migration eklememek. Migrate mevcut HasData yapılandırmasını ayrıca incelemez.
- Migration kullanılan ilişkisel veritabanında EnsureCreated çağırmak. Belgeler bunu açıkça önermez.
Sık Sorulan Sorular
UseSeeding her uygulama başlangıcında çalışır mı?
Hayır, yalnızca EF Core bir veritabanı başlatma ya da migration işlemi yaptığında çalışır: EnsureCreated, Migrate, dotnet ef database update ve bundle'lar. Uygulamanız başlangıçta Migrate çağırıyorsa seed kodu da her başlangıçta çalışır; bu yüzden kodun tekrar çalıştırılmaya dayanıklı olması gerekir. Çalışma zamanında migration uygulamanın artı ve eksileri production migration yazısında ele alınıyor.
HasData ile eklediğim kayıtlarda anahtar vermek zorunda mıyım?
Evet. Script veritabanına bağlanmadan üretildiği için birincil anahtar değeri, normalde veritabanı üretse bile verilmelidir. Anahtarı değiştirirseniz önceki veri silinir.
Birden çok sunucuda aynı anda Migrate çalışırsa seed iki kez eklenir mi?
EF Core 9'dan beri Migrate ve MigrateAsync veritabanı genelinde bir kilit alır ve bu kilit seed kodu dahil migration boyunca tutulur. Seed kodunuz verinin varlığını kontrol ediyorsa ikinci örnek veriyi zaten bulur. Kilitleme davranışı sağlayıcıya göre değiştiği için sağlayıcınızın belgelerine bakın.
Test verisi için hangi yöntemi kullanmalıyım?
Belgeler geçici test verisi için UseSeeding/UseAsyncSeeding'i önerir. Test veritabanını sıfırdan kuruyorsanız EnsureCreated ile birlikte kullanmak da mümkündür.
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 · Migration rehberi · Migration: SQL script ve bundle · Pending model changes hatası · Migration özelleştirme · EF Core ile test yazma