ORTA
Migrations (Göçler)
C# modelinde yaptığın her değişikliği (yeni tablo, sütun ekleme, tip değiştirme) veritabanına taşıyan versiyonlama sistemidir. Git commit'leri gibi düşün — her migration bir "DB commit".
Temel Komutlar
# İlk migration'ı oluştur
dotnet ef migrations add InitialCreate
# Veritabanına uygula
dotnet ef database update
# Yeni migration (ör. tablo ekleme)
dotnet ef migrations add AddOrdersTable
# Belirli bir migration'a geri dön (rollback)
dotnet ef database update AddOrdersTable
# Son migration'ı sil (henüz uygulanmamışsa)
dotnet ef migrations remove
# SQL script üret (production deploy için)
dotnet ef migrations script --idempotent -o migrate.sql
# Tüm migration'ların listesi
dotnet ef migrations list
Package Manager Console kullanıyorsan:
Add-Migration InitialCreate Update-Database Remove-Migration Script-Migration -Idempotent
Migration dosyası neye benzer?
// Migrations/20250115_InitialCreate.cs (EF otomatik üretir)
public partial class InitialCreate : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Categories",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Name = table.Column<string>(maxLength: 100, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Categories", x => x.Id);
});
migrationBuilder.CreateTable(
name: "Products",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Name = table.Column<string>(maxLength: 200, nullable: false),
CategoryId = table.Column<int>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Products", x => x.Id);
table.ForeignKey("FK_Products_Categories_CategoryId",
x => x.CategoryId, "Categories", "Id",
onDelete: ReferentialAction.Restrict);
});
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(name: "Products");
migrationBuilder.DropTable(name: "Categories");
}
}
// Migrations/20250115_InitialCreate.cs (EF otomatik üretir — Npgsql provider)
public partial class InitialCreate : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "categories",
columns: table => new
{
id = table.Column<int>(type: "integer", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy",
NpgsqlValueGenerationStrategy.IdentityAlwaysColumn),
name = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("pk_categories", x => x.id);
});
migrationBuilder.CreateTable(
name: "products",
columns: table => new
{
id = table.Column<int>(type: "integer", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy",
NpgsqlValueGenerationStrategy.IdentityAlwaysColumn),
name = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: false),
category_id = table.Column<int>(type: "integer", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("pk_products", x => x.id);
table.ForeignKey("fk_products_categories_category_id",
x => x.category_id, "categories", "id",
onDelete: ReferentialAction.Restrict);
});
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(name: "products");
migrationBuilder.DropTable(name: "categories");
}
}
Migration Bundle (EF Core 6+) — Production Deploy
# Bundle = tek çalıştırılabilir dosya. dotnet ef tool'u production'a kurulmaz.
dotnet ef migrations bundle --self-contained -o efbundle.exe
# Production'da çalıştır
./efbundle.exe --connection "Server=prod;Database=MyDb;..."
CI/CD Pipeline'da Migration
# Azure DevOps / GitHub Actions örneği
steps:
# 1. Migration bekleyen değişiklik var mı kontrolü
- run: dotnet ef migrations has-pending-model-changes --no-build
# 2. Idempotent script üret (her zaman güvenli — zaten uygulanmış olanları atlar)
- run: dotnet ef migrations script --idempotent --no-build -o $(Build.ArtifactStagingDirectory)/migrate.sql
# 3. Veya bundle üret
- run: dotnet ef migrations bundle --no-build -o $(Build.ArtifactStagingDirectory)/efbundle
Production'da Migration Stratejileri
| Strateji | Ne Zaman | Risk |
|---|---|---|
dotnet ef database update |
Development | Production'da kullanma |
--idempotent SQL script |
Küçük-orta proje | DBA review edebilir |
Migration Bundle (efbundle) |
Otomatik deploy | Hızlı ama review yok |
| Manual SQL + MigrationHistory insert | Enterprise / DBA kontrolü | Tam kontrol |
| Expand-Contract (Zero-downtime) | Büyük production sistemi | Downtime sıfır |
Data Loss Uyarısı: Sütun kaldırma/tip değiştirme migration'larında EF uyarı verir ama engellemez. Production'da her migration'ı önce test ortamında çalıştırılmalı.
Zero-Downtime Migration (Expand-Contract Pattern)
Sütun rename veya kaldırma gibi breaking change'leri downtime olmadan yapmak için 2 adımlı deploy:
Deploy 1 (Expand):
─ Yeni sütunu EKLE (nullable, default ile)
─ Kod hem eski hem yeni sütuna yazar (dual-write)
─ Background job: eski veriyi yeni sütuna kopyalar
Deploy 2 (Contract):
─ Kod artık sadece yeni sütunu kullanır
─ Eski sütunu DROP et (artık referans yok)
// Adım 1: Expand — yeni sütun ekle, eski duruyor
migrationBuilder.AddColumn<string>("FullName", "Users", nullable: true);
migrationBuilder.Sql("UPDATE Users SET FullName = FirstName + ' ' + LastName");
// Adım 2: Contract (ayrı migration, ayrı deploy!)
migrationBuilder.DropColumn("FirstName", "Users");
migrationBuilder.DropColumn("LastName", "Users");
Kural: Bir migration'da hem ADD hem DROP yapma. Her biri ayrı deploy olmalı — arada kodun her iki sütunu da desteklediğinden emin ol.
Migration Geri Alma (Rollback)
# Son migration'ı geri al (development)
dotnet ef database update PreviousMigrationName
# Migration dosyasını sil (henüz apply edilmemişse)
dotnet ef migrations remove
# Production'da rollback — Down() metodu çalışır:
dotnet ef migrations script CurrentMigration PreviousMigration --idempotent -o rollback.sql
# DBA bu script'i review edip çalıştırır
Down() metodu güvenilir mi? EF otomatik Down üretir ama veri kaybı olabilir (DROP COLUMN geri gelmez). Critical migration'larda Down()'ı her zaman manual kontrol et.
Migration Akışı (Görsel)
__EFMigrationsHistory tablosu (EF otomatik oluşturur):
| MigrationId | ProductVersion |
|---|---|
| 20250115083000_InitialCreate | 8.0.1 |
| 20250120140000_AddOrdersTable | 8.0.1 |
| 20250201091500_AddIndexToProducts | 8.0.1 |
PostgreSQL Migration Farkları
PostgreSQL Migration Farkları:
- Komutlar aynı:
dotnet ef migrations add,dotnet ef database update__EFMigrationsHistorytablosu PostgreSQL'de de aynı şekilde oluşur (publicschema'da)- Önemli farklar:
# PostgreSQL connection string ile migration:
dotnet ef database update -- --connection "Host=localhost;Database=mydb;Username=app;Password=***"
# Veya appsettings.json'dan otomatik alır
// Migration içinde provider-specific SQL:
protected override void Up(MigrationBuilder migrationBuilder)
{
// EF Core her iki provider için doğru SQL üretir:
migrationBuilder.CreateTable(
name: "products",
columns: table => new
{
Id = table.Column<int>(type: "integer", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy",
NpgsqlValueGenerationStrategy.IdentityAlwaysColumn),
Name = table.Column<string>(type: "text", nullable: false),
Price = table.Column<decimal>(type: "numeric(18,2)", nullable: false)
},
constraints: table => table.PrimaryKey("pk_products", x => x.Id));
// Provider-specific raw SQL (sadece PG'de çalışır):
if (migrationBuilder.ActiveProvider == "Npgsql.EntityFrameworkCore.PostgreSQL")
{
migrationBuilder.Sql("CREATE EXTENSION IF NOT EXISTS \"uuid-ossp\";");
migrationBuilder.Sql("CREATE EXTENSION IF NOT EXISTS \"pg_trgm\";");
}
}
PostgreSQL migration dikkat noktaları:
HasColumnType("nvarchar(max)")→ PG'de hata verir! Provider değiştirirsen tüm migration'ları yeniden oluşturUseSnakeCaseNamingConvention()eklediysen ilk migration'dan önce ekle, sonradan ekleme büyük rename migration'ı üretir- Extension'lar (
uuid-ossp,pg_trgm,hstore) ilk migration'daCREATE EXTENSIONile ekle- PostgreSQL transactional DDL destekler → migration başarısız olursa otomatik ROLLBACK (SQL Server'da bu garanti değil!)
Production'a Migration Deploy Checklist
🚨 Bu adımları atlama. Yanlış migration production veritabanını geri dönüşümsüz bozabilir.
| # | Adım | Komut / Aksiyon |
|---|---|---|
| 1 | Backup al | DB snapshot veya pg_dump / .bak backup |
| 2 | SQL script üret | dotnet ef migrations script --idempotent -o deploy.sql |
| 3 | Script'i incele | DROP, ALTER TYPE, data loss riski var mı? |
| 4 | Staging'de çalıştır | Aynı script'i staging DB'ye uygula, hata var mı? |
| 5 | Rollback planı yaz | Geri alma script'i hazırla: dotnet ef migrations script <önceki> <hedef> |
| 6 | Maintenance window | Gerekirse kısa downtime planla (rename, type change durumlarında) |
| 7 | Deploy et | Script'i production'a uygula (sqlcmd / psql) |
| 8 | Doğrula | SELECT * FROM __EFMigrationsHistory — son migration göründü mü? |
# İdempotent script (zaten uygulanmış migration'ları atlar):
dotnet ef migrations script --idempotent -o deploy.sql
# Belirli bir migration'a kadar:
dotnet ef migrations script InitialCreate AddProductIndex -o partial.sql
# Rollback script (AddProductIndex'ten InitialCreate'e geri dön):
dotnet ef migrations script AddProductIndex InitialCreate -o rollback.sql
Asla production'da
dotnet ef database updateçalıştırma. Bu komut CI/CD veya local dev içindir. Production'da her zaman idempotent SQL script kullan ve DBA review'dan geçir.