الگوهای بهینه در مدیریت EF Core Migrations
نویسنده: وحید نصیری
تاریخ: ۱۴۰۵/۰۵/۲۳ ۰۸:۴۰
آدرس: www.dntips.ir
چکیده: مکانیزم Migrations در فریمورک Entity Framework Core ابزاری قدرتمند برای تکامل تدریجی طرحواره پایگاهداده (Database Schema) همگام با مدل دامنه داتنت است. با این حال، تکیه بر تنظیمات پیشفرض در محیطهای عملیاتی سازمانی (Production) چالشهایی نظیر از دست رفتن دادهها (Data Loss)، عدم همخوانی نسخه مدل، قفل شدن جداول و ناسازگاری در معماریهای چندنسخهای ایجاد میکند. این مقاله راهکارهای استاندارد، الگوهای نامگذاری، تغییرات ساختاری ایمن، استراتژیهای استقرار در خطوط CI/CD، روشهای صحیح مقداردهی اولیه دادهها (Data Seeding) و مانیتورینگ سلامت را بررسی میکند.
Fix یا Update پس از چند ماه پیگیری تاریخچه را غیرممکن میسازد. از نامهایی شفاف، فعلمحور و معطوف به هدف تغییر استفاده کنید:AddUserTableAddMiddleNameToUserTableRemoveSalesDateFromClientOrderTableUser، تغییر ساختار جدول Sales و نگاشت توابع، فرآیند بازبینی کد (Code Review) و بازگردانی احتمالی (Rollback) را با اختلال مواجه میکند.[ستون اصلی] ──(EF Core Drop)──> [حذف کامل ستون و دادهها] ──(Add Column)──> [ایجاد ستون جدید با ساختار جدید]
RenameColumn()migrationBuilder.RenameColumn() استفاده کنید.نکته حیاتی: ویوها، پروسیجرها و ایندکسهای سفارشی که به نام قبلی وابستهاند، بهطور خودکار بهروزرسانی نمیشوند و باید در اسکریپت مایگریشن مدنظر قرار گیرند.
ModelSnapshot.cs است. دستکاری دستی فایلهای تولیدشده بدون درک ساختار اسنپشات، هماهنگی مدل و پایگاهداده را برهم میزند.dotnet ef migrations remove
dotnet ef database update <PreviousMigrationName> dotnet ef migrations remove
DbContext معمولاً در پروژهای مجزا (نظیر لایه Persistence یا Data) نسبت به پروژه اجرایی (API) قرار دارد. برای اجرای دستورات CLI باید پروژه استارتاپ را صراحتاً مشخص کنید:# اجرا از پوشه پروژه Data dotnet ef migrations add AddMiddleNameToUser --startup-project ../MyApp.ApiService dotnet ef database update --startup-project ../MyApp.ApiService
Database.Migrate() یا MigrateAsync() در متد آغازین برنامه (Program.cs) به دلایل زیر در محیطهای سازمانی منسوخ و پرخطر است:ALTER TABLE) برای کاربری وباپلیکیشن در دیتابیس.| روش استقرار | موارد کاربرد | مزایا |
| Migration Bundles | کانتینرها، خطوط ابری CI/CD | فایل اجرایی منفرد، مستقل از کد، سبک و بدون نیاز به نصب .NET SDK کامل |
| Idempotent SQL Scripts | خطوط سنتی، کنترل توسط DBA | اسکریپت شفاف، امکان بازبینی پیش از اجرا و بررسی جدول __EFMigrationsHistory |
dotnet ef migrations bundle --self-contained -r linux-x64 -o efbundle # اجرا در فرآیند استقرار ./efbundle --connection "Server=...;Database=...;"
dotnet ef migrations script --idempotent -o update.sql
OnModelCreating با استفاده از HasData() تعریف میشوند:protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Country>().HasData(
new Country { Id = 1, Name = "Iran", Code = "IR" },
new Country { Id = 2, Name = "Germany", Code = "DE" }
);
}HasData مقدار کلید اصلی (Primary Key) حتماً باید مشخص شود تا EF Core بتواند تغییرات رکوردها را در مایگریشنهای بعدی شناسایی کند. تغییر شناسه موجود معادل حذف و ثبت مجدد خواهد بود.EnsureCreatedAsync() با سیستم مایگریشنها پرهیز کنید، چرا که این متد جدول __EFMigrationsHistory را نادیده گرفته و پایگاهداده را از وضعیت سازگار با مایگریشن خارج میکند. الگوی صحیح، تفکیک منطق تست در یک کلاس مجزا و اجرای آن پس از اِعمال مایگریشنها در محیط Development است:public static class TestDataSeeder
{
public static async Task SeedAsync(AppDbContext db)
{
if (!await db.Users.AnyAsync())
{
db.Users.AddRange(
new User { Name = "Alice", Email = "alice@example.com" },
new User { Name = "Bob", Email = "bob@example.com" }
);
await db.SaveChangesAsync();
}
}
}
// Program.cs
if (app.Environment.IsDevelopment())
{
using var scope = app.Services.CreateScope();
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
await db.Database.MigrateAsync();
await TestDataSeeder.SeedAsync(db);
}builder.Services.AddHealthChecks()
.AddCheck<MigrationHealthCheck>("database_migrations");
public class MigrationHealthCheck(AppDbContext db) : IHealthCheck
{
public async Task<HealthCheckResult> CheckHealthAsync(
HealthCheckContext context,
CancellationToken ct = default)
{
var pending = (await db.Database.GetPendingMigrationsAsync(ct)).ToList();
return pending.Count == 0
? HealthCheckResult.Healthy("Database schema is fully synchronized.")
: HealthCheckResult.Degraded($"{pending.Count} pending migration(s): {string.Join(", ", pending)}");
}
}[مایگریشن ۱] ──> [مایگریشن ۲] ──> ... ──> [مایگریشن ۱۰۰]
↓
[یکپارچهسازی (Squash)]
↓
[مایگریشن پایه نهایی با ثبت وضعیت در تاریخچه]InitialCreate بسازید:__EFMigrationsHistory را چک میکند؛ این جدول نام تکتک مایگریشنهای قدیمی را دارد اما مایگریشن جدید شما (InitialCreate) را ندارد. در نتیجه تلاش میکند تمام جداول را از اول بسازد و با خطای Table already exists یا تخریب دیتابیس متوقف میشود.dotnet ef migrations add CheckpointSync
20260814050000_CheckpointSync) را یادداشت کنید.__EFMigrationsHistory ثبت شود:dotnet ef database update
Migrations پروژه (از جمله فایل مایگریشن مرحله ۱) را حذف کنید. دقت کنید: فقط فایل Snapshot یا کدهای اصلی دیتابیس را دستکاری نکنید، صرفاً فایلهای لیست مایگریشنها را پاک کنید.dotnet ef migrations add InitialBaseline
20260814050000_CheckpointSync) شود.__EFMigrationsHistory را بررسی میکند. میبیند که رکورد 20260814050000_CheckpointSync قبلاً ثبت و اجرا شده است؛ بنابراین هیچ کدی اجرا نمیکند و دیتابیس دستنخورده باقی میماند.