EF Core migration, uygulamanızdaki veri modeli değiştikçe veritabanı şemasını adım adım güncelleyen ve bunu yaparken mevcut verileri korumayı hedefleyen mekanizmadır. Entity Framework Core'da modele bir sınıf ya da property eklediğinizde dotnet ef migrations add komutuyla bir migration üretir, ardından dotnet ef database update ile bu değişikliği veritabanına uygularsınız. EF Core hangi migration'ların uygulandığını veritabanındaki özel bir geçmiş tablosunda tuttuğu için her seferinde yalnızca eksik olanları çalıştırır.

Bu yazı migration'ın günlük geliştirme akışını anlatır: ilk migration'ı oluşturmak, üretilen dosyaları anlamak, migration kaldırmak, listelemek, unutulmuş model değişikliklerini yakalamak ve gerektiğinde tüm geçmişi sıfırlamak. Production veritabanına uygulama stratejileri (SQL script, bundle, çalışma zamanında Migrate()) ayrı bir yazının konusu: EF Core migration'ı production'a uygulama. Migration kodunu elle düzenleme ve ham SQL ekleme ise migration özelleştirme yazısında anlatılıyor. Serinin tamamı için EF Core öğrenme rehberi sayfasına bakabilirsiniz.

Migration mı, scaffold mu, EnsureCreated mı?

Migration mı, scaffold mu, EnsureCreated mı? Migrations, Scaffolding (tersine mühendislik), EnsureCreated / EnsureDeleted

EF Core, modelinizle veritabanı şemasını senkron tutmak için iki ana yol sunar ve seçim tek bir soruya dayanır: doğruluğun kaynağı hangisi? Eğer C# sınıflarınız ve OnModelCreating içindeki yapılandırma esas kabul ediliyorsa migration kullanırsınız; model değiştikçe EF Core şemaya karşılık gelen değişiklikleri üretir. Eğer veritabanı şeması esas kabul ediliyorsa, yani tabloları bir DBA ya da başka bir ekip yönetiyorsa, tersine mühendislik (scaffolding) ile şemadan DbContext ve entity sınıfları üretirsiniz; bu yol scaffold-dbcontext yazısında anlatılıyor.

Üçüncü bir seçenek olan EnsureCreatedAsync ve EnsureDeletedAsync metotları da modelden şema oluşturabilir. Ancak belgeler bunları özellikle test, prototip ve veritabanının silinip yeniden oluşturulmasının sorun olmadığı durumlar için konumlandırır. EnsureCreatedAsync şemayı migration'ları atlayarak kurduğu için ikisi birlikte iyi çalışmaz; daha sonra migration'a geçmek kolay değildir ve en basit yol veritabanını silip migration'larla baştan oluşturmaktır. İleride migration kullanma ihtimaliniz varsa doğrudan migration ile başlamak en az sürprizli yoldur.

YaklaşımDoğruluğun kaynağıTipik kullanım
MigrationsEF Core modeliŞeması uygulamayla birlikte gelişen projeler
Scaffolding (tersine mühendislik)Veritabanı şemasıVar olan veya başka ekibin yönettiği veritabanı
EnsureCreated / EnsureDeletedEF Core modeli (geçmişsiz)Test, prototip, geçici veri

Migration nasıl çalışır?

Mekanizma iki parçadan oluşur. Birincisi model karşılaştırmasıdır: yeni bir migration eklediğinizde EF Core mevcut modeli, önceki migration'dan kalan model anlık görüntüsüyle (model snapshot) karşılaştırır, aradaki farkı bulur ve bu farkı uygulayacak C# kaynak dosyalarını üretir. Bu dosyalar sıradan kaynak kodu gibi projenizde durur ve kaynak kontrolüne (git) eklenir. Yani migration, veritabanının o anki içeriğine bakılarak değil, iki model hâli karşılaştırılarak üretilir. Bu nedenle snapshot dosyası, migration dizisinin en az migration dosyaları kadar önemli bir parçasıdır.

İkincisi uygulama kaydıdır: bir migration veritabanına uygulandığında EF Core bunu varsayılan adı __EFMigrationsHistory olan bir geçmiş tablosuna yazar. Sonraki uygulamada bu tabloya bakarak hangi migration'ların zaten çalıştığını bilir ve yalnızca yenilerini uygular. Geçmiş tablosunun adı ve şeması değiştirilebilir; bu ayrıntı özelleştirme yazısında var.

Araçları hazırlamak

Migration komutları EF Core komut satırı araçlarıyla çalışır. Belgeler genel olarak tüm platformlarda çalışan .NET CLI araçlarını (dotnet ef) önerir; Visual Studio içinde çalışmayı seviyorsanız ya da EF6 migration deneyiminiz varsa Package Manager Console (PMC) araçlarını da kullanabilirsiniz. Araçların kurulumu, Microsoft.EntityFrameworkCore.Design paketinin projeye eklenmesi ve ortak seçenekler dotnet ef komutları yazısında, paketlerin genel tablosu ise EF Core kurulumu yazısında anlatılıyor. Araçların DbContext nesnenizi nasıl oluşturduğunu merak ediyorsanız design-time DbContext factory yazısına bakın.

İlk migration: InitialCreate

Belgelerin başlangıç örneğinde yalnızca kimliği ve adı olan bir Blog sınıfı vardır. Geliştirme sırasında belki EnsureCreated ile hızlı ilerlediniz; ancak uygulama production'a yaklaştığında şemayı veritabanını silmeden geliştirebilmeniz gerekir. İşte bu noktada ilk migration'ı eklersiniz:

csharp
public class Blog
{
    public int Id { get; set; }
    public string Name { get; set; }
}
bash
dotnet ef migrations add InitialCreate
powershell
Add-Migration InitialCreate

Komut projenizde Migrations adında bir klasör oluşturur ve birkaç dosya üretir. Belgelerin tavsiyesi, EF Core'un tam olarak ne ürettiğini incelemeniz ve gerekiyorsa düzeltmenizdir. Ardından veritabanını ve şemayı bu migration'dan oluşturursunuz:

bash
dotnet ef database update
powershell
Update-Database

Bu kadar: tek satır SQL yazmadan veritabanı ve tablolar oluşur. Yine de belgeler bu uygulama biçiminin yerel geliştirme için ideal olduğunu, production ortamları için daha az uygun olduğunu vurgular. Bunun nedenleri, yani SQL'i gözden geçirememek ve sunucuya SDK ile kaynak kodu koyma zorunluluğu, production yazısında ayrıntılı olarak ele alınıyor.

Üretilen üç dosya

Her migrations add komutu Migrations klasörüne üç dosya ekler ya da günceller. Bunları tanımak, hem kod incelemesinde hem de sorun giderirken işinizi kolaylaştırır:

  • XXXXXXXXXXXXXX_AddBlogCreatedTimestamp.cs: Ana migration dosyası. Migration'ı uygulamak için gereken işlemler Up metodunda, geri almak için gerekenler Down metodunda bulunur.
  • XXXXXXXXXXXXXX_AddBlogCreatedTimestamp.Designer.cs: EF Core'un kullandığı meta veri dosyası. O migration noktasındaki modeli temsil eder ve migration kaldırılırken kullanılır.
  • MyContextModelSnapshot.cs: Modelin güncel anlık görüntüsü. Bir sonraki migration eklendiğinde neyin değiştiğini bulmak için bununla karşılaştırma yapılır.

Dosya adının başındaki zaman damgası migration'ları kronolojik sırada tutar; klasöre baktığınızda şemanın nasıl geliştiğini sırasıyla görürsünüz. Snapshot dosyası tek ve ortaktır; her migration onu günceller. Belgeler snapshot'ı elle düzenlemeyi önermez; takım ortamı sayfası da snapshot'ın başka bir daldaki değişiklikleri içermemesinin sonraki migration'larda çeşitli bozulmalara yol açabileceği konusunda uyarır. Snapshot eksik olduğunda neler olduğu pending model changes yazısında anlatılıyor.

Modeli geliştirmek: ikinci migration

Birkaç gün sonra bloglara oluşturulma zamanı eklemeniz istendiğini düşünün. Modeli değiştirirsiniz; artık modeliniz ile veritabanınız senkron değildir ve şemaya yeni bir kolon eklenmesi gerekir:

csharp
public class Blog
{
    public int Id { get; set; }
    public string Name { get; set; }
    public DateTime CreatedTimestamp { get; set; }
}
bash
dotnet ef migrations add AddBlogCreatedTimestamp
dotnet ef database update

Migration'a açıklayıcı bir ad vermek önemlidir; belgeler migration adını bir sürüm kontrol sistemindeki commit mesajına benzetir. AddBlogCreatedTimestamp gibi bir ad, aylar sonra geçmişe baktığınızda neyin neden değiştiğini anlatır. Bu ilk migration olmadığı için EF Core güncel modeli, kolon eklenmeden önceki snapshot ile karşılaştırır, bir kolonun eklendiğini tespit eder ve uygun migration'ı üretir. database update çalıştığında bu kez veritabanının zaten var olduğunu görür; ilk migration geçmiş tablosunda kayıtlı olduğu için yalnızca yeni migration uygulanır.

Klasör ve namespace seçimi

Migration dosyalarını taşıyabilir ve namespace'lerini elle değiştirebilirsiniz. Yeni migration'lar son migration'ın yanına, aynı klasöre oluşturulur. Klasörü üretim anında belirlemek isterseniz --output-dir seçeneğini, namespace'i klasörden bağımsız belirlemek isterseniz --namespace seçeneğini kullanırsınız. PMC karşılıkları -OutputDir ve -Namespace parametreleridir.

bash
dotnet ef migrations add InitialCreate --output-dir Your/Directory
powershell
Add-Migration InitialCreate -OutputDir Your\Directory

Migration'ları tamamen ayrı bir projede tutmak da mümkündür ve WinUI, .NET MAUI, Blazor WebAssembly gibi platforma özgü uygulamalarda önerilen düzen budur. Bu yapı migration özelleştirme yazısında anlatılıyor.

Üretilen kodu her zaman inceleyin

EF Core çoğu zaman doğru migration üretir, ama belgeler kodu her seferinde gözden geçirmenizi ister. En bilinen tuzak property yeniden adlandırmadır: Name property'sini FullName yaptığınızda EF Core bunun bir yeniden adlandırma mı yoksa bir kolonu silip başka bir kolon ekleme mi olduğunu bilemez ve varsayılan olarak DropColumn ile AddColumn üretir. Bu migration olduğu gibi uygulanırsa o kolondaki tüm veriler kaybolur. Çözüm, üretilen iki işlemi RenameColumn ile değiştirmektir; ayrıntılı örnek özelleştirme yazısında var.

Migration kaldırmak

Migration kaldırmak: Hiçbir yere uygulanmadıysa, Yalnızca yerel, atılabilir geliştirme veritabanına uygulandıysa, Paylaşılan, test veya production veritabanına uygulandıysa

Bazen migration'ı ekledikten sonra modelde ek değişiklikler yapmanız gerektiğini fark edersiniz. Son migration'ı kaldırmak için aşağıdaki komut kullanılır; kaldırdıktan sonra modeli düzeltip migration'ı yeniden eklersiniz. Komut snapshot'ı da önceki duruma geri getirir:

bash
dotnet ef migrations remove
powershell
Remove-Migration

Burada kritik olan, migration'ın bir veritabanına uygulanıp uygulanmadığıdır. Belgeler üç durumu ayırır:

  • Hiçbir yere uygulanmadıysa: migrations remove yeterlidir.
  • Yalnızca yerel, atılabilir geliştirme veritabanına uygulandıysa: Önce veritabanını bir önceki migration'a geri alın, sonra migration'ı kaldırın. İlk migration'ı kaldırıyorsanız hedef olarak 0 verin. --force seçeneği iki adımı birlikte yapar.
  • Paylaşılan, test veya production veritabanına uygulandıysa: Migration'ı silmeyin. Genellikle onu projede bırakıp düzeltici yeni bir migration eklemek doğru yoldur. Planlı bir geri dönüş gerekiyorsa, bunu orijinal migration kodu hâlâ elinizdeyken yapın ve uygulama ile veritabanı dağıtımını birlikte koordine edin.
bash
dotnet ef database update PreviousMigration
dotnet ef migrations remove

# veya iki adımı birlikte yapmak için
dotnet ef migrations remove --force

Araçlar yalnızca en son migration'ı kaldırır. Dizinin ortasından bir migration silip snapshot'ı elle düzenlemeye çalışmayın. O migration ve sonrasındaki tüm migration'lar hiçbir yere uygulanmamış ve yayımlanmamışsa, sonrakileri ters sırayla kaldırın, istenmeyen migration'ı kaldırın ve korunacak model değişikliklerini yeniden migration olarak üretin. Migration'lar farklı dallarda oluştuysa takım ortamı için tarif edilen ayrı akışı izlemeniz gerekir; bu da özelleştirme yazısında anlatılıyor.

Migration'ları listelemek ve durumu kodla sorgulamak

Projedeki migration'ları dotnet ef migrations list (PMC'de Get-Migration) ile listeleyebilirsiniz. Aynı bilgiyi uygulama kodundan da alabilirsiniz; bu, örneğin bir yönetim ekranında veya sağlık kontrolünde veritabanının hangi sürümde olduğunu göstermek için kullanışlıdır:

csharp
var allMigrations = context.Database.GetMigrations();
var appliedMigrations = await context.Database.GetAppliedMigrationsAsync();
var pendingMigrations = await context.Database.GetPendingMigrationsAsync();

Burada sık karıştırılan bir nokta var: GetPendingMigrationsAsync, migration assembly'sindeki migration'ları veritabanının geçmiş tablosundaki kayıtlarla karşılaştırır. Yani “kodda olup veritabanında olmayan migration” bulur. Modelde yapıp henüz migration'a dönüştürmediğiniz değişiklikleri ise görmez. Onun için bir sonraki bölümdeki kontrolü kullanmanız gerekir.

Unutulan migration'ı yakalamak: has-pending-model-changes

Bir property ekleyip migration eklemeyi unutmak, özellikle ekip içinde, çok yaygın bir hatadır. EF Core 8 ile gelen dotnet ef migrations has-pending-model-changes komutu, son migration'dan bu yana modelde değişiklik yapılıp yapılmadığını kontrol eder. Aynı kontrolü kodda context.Database.HasPendingModelChanges() ile yapabilirsiniz; belgeler bunun, migration eklemeyi unuttuğunuzda başarısız olan bir birim testi yazmak için kullanılabileceğini söyler. CI hattına bu komutu eklemek, hatayı deploy anında değil pull request aşamasında yakalamanızı sağlar.

bash
dotnet ef migrations has-pending-model-changes

Bu kontrol EF Core 9'dan bu yana daha da önemli hâle geldi: model bekleyen değişiklik içeriyorsa Migrate(), MigrateAsync() ve dotnet ef database update artık bir istisna fırlatıyor. Hatanın nedenleri ve çözümleri PendingModelChangesWarning yazısında ayrıntılı olarak anlatılıyor.

Modelin bir kısmını migration dışında bırakmak

Bazen bir DbContext başka bir context'in yönettiği bir tipe referans verir. İki context de aynı tablo için migration üretmeye kalkarsa çakışma olur. Bunu önlemek için tabloyu context'lerden birinin migration'larından hariç tutabilirsiniz. Belgelerdeki örnek, ASP.NET Core Identity'nin kullanıcı tablosunu bu şekilde dışarıda bırakır; tablo modelde kalır, sorgularda kullanılabilir, ama bu context'in migration'ları onu oluşturmaya çalışmaz:

csharp
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<IdentityUser>()
        .ToTable("AspNetUsers", t => t.ExcludeFromMigrations());
}

Tüm migration'ları sıfırlamak ve squash

Tüm migration'ları sıfırlamak ve squash: Bir şeyler ters giderse diye veritabanınızı yedekleyin (veritabanı yedekleme..., Veritabanında migration geçmiş tablosundaki tüm satırları...

Bazı uç durumlarda tüm migration'ları silip baştan başlamak gerekebilir. Veri önemli değilse yol basittir: Migrations klasörünü silip veritabanını düşürürsünüz, ardından tüm güncel şemayı içeren yeni bir başlangıç migration'ı oluşturursunuz. Veriyi koruyarak tüm geçmişi tek bir migration'a indirmeye ise squash denir. EF Core şu anda bunun için otomatik bir komut sunmuyor; belgeler adımları şöyle sıralar:

  • Bir şeyler ters giderse diye veritabanınızı yedekleyin (veritabanı yedekleme stratejileri).
  • Veritabanında migration geçmiş tablosundaki tüm satırları silin (SQL Server'da örneğin DELETE FROM [__EFMigrationsHistory]).
  • Migrations klasörünü silin.
  • Yeni bir migration oluşturun ve onun için dotnet ef migrations script ile SQL script üretin.
  • Tablolar zaten var olduğu için, ilk migration'ın uygulanmış olduğunu kaydeden tek bir satırı geçmiş tablosuna ekleyin. Bu ekleme SQL'i, üretilen script'in son işlemidir; değerleri kendi migration adınız ve EF sürümünüzle güncelleyin.
sql
INSERT INTO [__EFMigrationsHistory] ([MIGRATIONID], [PRODUCTVERSION])
VALUES (N'<full_migration_timestamp_and_name>', N'<EF_version>');

EF Core 11'de gelecek kolaylıklar (geliştirme aşamasında)

Belgeler, geliştirme aşamasındaki EF Core 11 için migration tarafında bazı yenilikleri şimdiden anlatıyor. Örneğin dotnet ef database update komutuna --add seçeneği ekleniyor: migration'ı tek adımda üretip Roslyn ile çalışma zamanında derleyerek hemen uygulamak için. Ayrıca migrations remove komutuna --connection ve veritabanına bağlanmadan kaldırmak için --offline seçenekleri geliyor; belgelere göre --offline ile --force birlikte kullanılamıyor. EF Core 11 henüz geliştirme aşamasında olduğundan bunlar EF Core 10 projelerinde yoktur; ayrıntı için Microsoft Learn: What's New in EF Core 11 sayfasına bakın.

Sık yapılan hatalar

  • Migration'ı inceleme yapmadan uygulamak: yeniden adlandırma silme ve ekleme olarak üretilmiş olabilir.
  • EnsureCreated ile oluşturulmuş bir veritabanına sonradan migration uygulamaya çalışmak. İkisi birlikte iyi çalışmaz.
  • Snapshot dosyasını veya Migrations klasörünü kaynak kontrolüne eklememek.
  • Production'a uygulanmış bir migration'ı migrations remove ile silmek.
  • GetPendingMigrationsAsync boş döndü diye modelin güncel olduğunu sanmak; o yalnızca uygulanmamış migration'lara bakar.
  • Model değişikliğinden sonra migration eklemeyi unutmak; bunu CI'da has-pending-model-changes ile yakalayın.

Sık Sorulan Sorular

Migration eklemek için veritabanı bağlantısı gerekir mi?

Migration üretimi, güncel model ile snapshot dosyasının karşılaştırılmasıyla yapılır; veritabanının içeriğine bakılmaz. Ancak araçlar DbContext nesnenizi oluşturmak için uygulama kodunuzu çalıştırır, bu yüzden context yapılandırmasının hatasız oluşturulabilmesi gerekir. database update ise elbette veritabanına bağlanır.

Migration dosyalarını git'e eklemeli miyim?

Evet. Belgelere göre migration dosyaları ve snapshot, projenizin diğer kaynak dosyaları gibi kaynak kontrolünde tutulur. Belgeler, en az bir migration olduğu hâlde snapshot'ın eksik olmasını EF Core 9 sonrası migration uygulanırken alınan bekleyen model değişikliği hatasının nedenlerinden biri olarak sayar.

Bir migration'ı geri almak için ne yapmalıyım?

Yerel veritabanında dotnet ef database update komutuna hedef olarak önceki migration'ın adını verirsiniz; komut daha yeni migration'ların Down işlemlerini çalıştırır. Hedef 0 ise tüm migration'lar geri alınır. Production'da geri dönüş için SQL script ve bundle seçenekleri production yazısında anlatılıyor.

Birden fazla DbContext varsa ne olur?

Projede birden fazla context sınıfı varsa --context seçeneği zorunludur; her context kendi migration dizisine sahip olur. Aynı tabloya iki context'ten referans veriliyorsa ExcludeFromMigrations ile çakışmayı önleyebilirsiniz.

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'ı production'a uygulama · Migration özelleştirme ve ham SQL · Pending model changes hatası · dotnet ef komutları · Scaffold-DbContext