Skip to main content
Proje code-first EF Core kullanır. Migration’lar Infrastructure.EFCore/Migrations/ altında durur; runtime’da MigrationService (bir IHostedService) tarafından otomatik uygulanır.

Migration üretme

dotnet ef komutları DbContext’in bulunduğu projede (Infrastructure.EFCore) çalışır, ama configuration’ı (connection string) okuyabilmek için startup projesi (API) gösterilir:
dotnet ef migrations add <MigrationAdi> \
  --project    src/DiyanetCleanArchitecture.Infrastructure.EFCore \
  --startup-project src/DiyanetCleanArchitecture.API
Migration’ı geri almak (henüz DB’ye uygulanmadıysa):
dotnet ef migrations remove \
  --project    src/DiyanetCleanArchitecture.Infrastructure.EFCore \
  --startup-project src/DiyanetCleanArchitecture.API
İdempotent SQL script üretmek (DBA’nın manuel uyguladığı senaryo için):
dotnet ef migrations script --idempotent \
  --project    src/DiyanetCleanArchitecture.Infrastructure.EFCore \
  --startup-project src/DiyanetCleanArchitecture.API \
  --output     migration.sql

DesignTimeDbContextFactory

dotnet ef design-time’da çalışırken DI container yoktur — runtime’daki IMediator, IEventBus, IAuditUserContext mevcut değildir. Bu yüzden DiyanetCleanArchitectureDesignTimeDbContextFactory, no-op dummy’lerle DbContext kurar:
public class DiyanetCleanArchitectureDesignTimeDbContextFactory
    : DesignTimeDbContextFactoryBase<DiyanetCleanArchitectureDbContext>
{
    protected override DiyanetCleanArchitectureDbContext CreateNewInstance(
        DbContextOptions<DiyanetCleanArchitectureDbContext> options)
        => new(options, new NoMediator(), new NoEventBus(),
               new AuditInterceptor(new NoAuditUserContext()));
}
NoAuditUserContext her zaman SystemActor.Id döner — design-time bir şekilde SaveChanges tetiklerse bile audit kolonlarına sağlam bir değer yazılır, exception fırlamaz. Base sınıf (DesignTimeDbContextFactoryBase) connection string’i API projesinin appsettings’inden okur:
private const string ConnectionStringName = "DiyanetCleanArchitectureDatabaseConnection";
private readonly string BasePath =
    Path.Combine(Directory.GetCurrentDirectory(), "../DiyanetCleanArchitecture.API");

// appsettings.json + appsettings.{ASPNETCORE_ENVIRONMENT}.json + env değişkenleri
optionsBuilder.UseNpgsql(connectionString)
              .UseSnakeCaseNamingConvention();
Komutu Infrastructure.EFCore klasöründen çalıştırın; BasePath ../DiyanetCleanArchitecture.API’ye göre relatiftir. Connection string boşsa factory anlamlı bir hata fırlatır.

Migration uygulama — MigrationService

Runtime’da migration MigrationService : IHostedService ile uygulanır. Sadece config’te AutoDeploy=true ise çalışır:
public async Task StartAsync(CancellationToken cancellationToken)
{
    if (_options.CurrentValue.AutoDeploy == false)
    {
        _logger.LogInformation("📢 Migration servisi kapalı.");
        return;
    }

    using var scope = _serviceProvider.CreateScope();
    var dbContext = scope.ServiceProvider.GetRequiredService<DiyanetCleanArchitectureDbContext>();

    int retryCount = 0;
    while (retryCount < MaxRetryCount && !cancellationToken.IsCancellationRequested) // MaxRetryCount = 3
    {
        try
        {
            if (!await dbContext.Database.CanConnectAsync(cancellationToken))
                _logger.LogWarning("🆕 Veritabanı bulunamadı. İlk kurulum başlatılıyor...");

            await dbContext.Database.MigrateAsync(cancellationToken);
            return;
        }
        catch (Exception ex)
        {
            retryCount++;
            if (retryCount >= MaxRetryCount) throw;
            await Task.Delay(TimeSpan.FromSeconds(5), cancellationToken);
        }
    }
}
Özellikler:
  • AutoDeploy guard: Kapalıysa hiçbir şey yapmaz.
  • Retry: En fazla 3 deneme, her başarısızlıkta 5 sn bekler. 3. denemede de patlarsa exception yeniden fırlatılır (uygulama başlamaz).
  • Database.MigrateAsync: Bekleyen tüm migration’ları uygular; DB yoksa oluşturur.
Konfigürasyon (appsettings.json):
{
  "Services": {
    "Migration": {
      "AutoDeploy": true
    }
  }
}
MigrationService, AddInfrastructureEFCore içinde DevDataSeeder ve DistrictSeeder ile birlikte hosted service olarak kaydedilir.

Docker’da migration

Compose senaryolarında API container’ı AutoDeploy=true ile açılır; postgres servisi henüz hazır değilse MigrationService’in retry’ı (3×5 sn) köprü görevi görür. Tek instance’ta bu yeterlidir.
Çok-instance (replica) prod’da her replica MigrateAsync çalıştırırsa yarış oluşabilir. Bu durumda ya tek bir “migration job” container’ı AutoDeploy=true ile açılır, diğer API replica’larında AutoDeploy=false bırakılır; ya da migration CI/CD adımında idempotent SQL script ile uygulanır.

Migration listesi

Migrations/ klasöründeki migration’lar (kronolojik — uygulama sırası budur):
Migrationİçerik
20260506111334_initialTemel şema (User, Role, Organization, Location…)
20260508073844_RolePermissionFixRolePermission düzeltmesi
20260513115344_userchangeUser aggregate değişiklikleri
20260514190633_ContentModulesFaq, Announcement, Center, SiteSettings
20260518115056_CitizenAggregateCitizen (vatandaş portalı) aggregate’i
20260518202850_RolePermissionsRevampRolePermission Guid PK + partial unique index
20260519130903_OutboxInboxMessagingSchemaMassTransit messaging şeması (outbox/inbox)
20260522100332_BasvuruAggregatesBağış & Etkinlik başvuru + plan aggregate’leri
20260523185247_ProfileImageAndLegalDocumentsProfileImage + LegalDocument/Version
20260524132444_AddAdminNotificationsAdminNotification + per-user okuma kaydı
20260525072317_AddSupportTicketsSupportTicket aggregate’i
20260525073703_AddSupportTicketPermissionsDestek talebi izinleri (seed)
20260531192628_AddAnnouncementCoverImageDuyuru kapak görseli
Model değişikliklerinin tutarlılığı Migrations/DiyanetCleanArchitectureDbContextModelSnapshot.cs ile takip edilir — bu dosya elle düzenlenmez, EF tarafından yönetilir.

İlgili

DbContext & Repository

DbSet’ler, OnModelCreating, EntityTypeConfiguration örnekleri.

Seed Data

Migration HasData seed’leri ve hosted seeder’lar.

Soft Delete

Partial unique index (deleted_at IS NULL) migration’larda nasıl üretilir.

Data Genel Bakış

Katman rolü ve klasör yapısı.