using ElectionSystem.Api.Infrastructure.Auditing; using ElectionSystem.Api.Modules.Identity.Domain; using ElectionSystem.Api.Modules.Organization.Domain; using ElectionSystem.Api.Modules.Voters.Domain; using ElectionSystem.Api.Modules.Candidates.Domain; using ElectionSystem.Api.Modules.Election.Domain; using ElectionSystem.Api.Modules.OfflineCounting.Domain; using Microsoft.EntityFrameworkCore; using Microsoft.AspNetCore.Identity.EntityFrameworkCore; using Microsoft.AspNetCore.Identity; namespace ElectionSystem.Api.Infrastructure.Persistence; public sealed class ElectionDbContext(DbContextOptions options) : IdentityDbContext(options) { public DbSet OrganizationUnits => Set(); public DbSet AdministrativeRegions => Set(); public DbSet UserRegionalRoleAssignments => Set(); public DbSet CountingStations => Set(); public DbSet UserCountingStationAssignments => Set(); public DbSet Voters => Set(); public DbSet VoterRegistrationAggregates => Set(); public DbSet Candidates => Set(); public DbSet ElectionActivities => Set(); public DbSet ElectionContests => Set(); public DbSet ElectionOptions => Set(); public DbSet ElectionCountingStations => Set(); public DbSet OfflineCountingSessions => Set(); public DbSet OfflineCountSubmissions => Set(); public DbSet OfflineCountTallies => Set(); public DbSet AuditEntries => Set(); protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); modelBuilder.Entity(entity => { entity.ToTable("identity_users"); entity.Property(x => x.UserName).HasMaxLength(128).IsRequired(); entity.Property(x => x.DisplayName).HasMaxLength(128).IsRequired(); entity.Property(x => x.ExternalSubject).HasMaxLength(256); entity.HasIndex(x => x.UserName).IsUnique(); entity.HasIndex(x => x.ExternalSubject).IsUnique(); }); modelBuilder.Entity().ToTable("identity_roles"); modelBuilder.Entity>().ToTable("identity_user_roles"); modelBuilder.Entity>().ToTable("identity_user_claims"); modelBuilder.Entity>().ToTable("identity_user_logins"); modelBuilder.Entity>().ToTable("identity_user_tokens"); modelBuilder.Entity>().ToTable("identity_role_claims"); modelBuilder.Entity(entity => { entity.ToTable("administrative_regions"); entity.Property(x => x.Code).HasMaxLength(32).IsRequired(); entity.Property(x => x.Name).HasMaxLength(200).IsRequired(); entity.Property(x => x.Source).HasMaxLength(128).IsRequired(); entity.Property(x => x.SourceVersion).HasMaxLength(64).IsRequired(); entity.HasIndex(x => new { x.Code, x.SourceVersion }).IsUnique(); entity.HasIndex(x => new { x.ParentId, x.Level, x.IsActive }); entity.HasOne().WithMany().HasForeignKey(x => x.ParentId).OnDelete(DeleteBehavior.Restrict); }); modelBuilder.Entity(entity => { entity.ToTable("identity_user_regional_roles"); entity.HasIndex(x => new { x.UserId, x.Role, x.RegionId }).IsUnique(); entity.HasOne().WithMany().HasForeignKey(x => x.UserId).OnDelete(DeleteBehavior.Cascade); entity.HasOne().WithMany().HasForeignKey(x => x.RegionId).OnDelete(DeleteBehavior.Restrict); }); modelBuilder.Entity(entity => { entity.ToTable("counting_stations"); entity.Property(x => x.Name).HasMaxLength(200).IsRequired(); entity.Property(x => x.Code).HasMaxLength(64).IsRequired(); entity.HasIndex(x => x.Code).IsUnique(); entity.HasIndex(x => x.RegionId); entity.HasOne().WithMany().HasForeignKey(x => x.RegionId).OnDelete(DeleteBehavior.Restrict); }); modelBuilder.Entity(entity => { entity.ToTable("identity_user_counting_stations"); entity.HasIndex(x => new { x.UserId, x.CountingStationId }).IsUnique(); entity.HasOne().WithMany().HasForeignKey(x => x.UserId).OnDelete(DeleteBehavior.Cascade); entity.HasOne().WithMany().HasForeignKey(x => x.CountingStationId).OnDelete(DeleteBehavior.Cascade); }); modelBuilder.Entity(entity => { entity.ToTable("voter_profiles"); entity.Property(x => x.VoterNumber).HasMaxLength(64).IsRequired(); entity.Property(x => x.DisplayName).HasMaxLength(128).IsRequired(); entity.Property(x => x.DisabledReason).HasMaxLength(500); entity.HasIndex(x => x.VoterNumber).IsUnique(); entity.HasIndex(x => new { x.RegionId, x.Status, x.DisplayName }); entity.HasIndex(x => x.UserId).IsUnique(); entity.HasOne().WithMany().HasForeignKey(x => x.RegionId).OnDelete(DeleteBehavior.Restrict); entity.HasOne().WithMany().HasForeignKey(x => x.UserId).OnDelete(DeleteBehavior.Restrict); }); modelBuilder.Entity(entity => { entity.ToTable("voter_registration_aggregates"); entity.Property(x => x.Reason).HasMaxLength(500).IsRequired(); entity.HasIndex(x => x.RegionId).IsUnique(); entity.HasOne().WithMany().HasForeignKey(x => x.RegionId).OnDelete(DeleteBehavior.Restrict); entity.HasOne().WithMany().HasForeignKey(x => x.SetByUserId).OnDelete(DeleteBehavior.Restrict); }); modelBuilder.Entity(entity => { entity.ToTable("candidate_profiles"); entity.Property(x => x.DisplayName).HasMaxLength(128).IsRequired(); entity.Property(x => x.NominationSummary).HasMaxLength(4000); entity.Property(x => x.StatusReason).HasMaxLength(500); entity.HasIndex(x => new { x.RegionId, x.Status, x.DisplayName }); entity.HasOne().WithMany().HasForeignKey(x => x.RegionId).OnDelete(DeleteBehavior.Restrict); }); modelBuilder.Entity(entity => { entity.ToTable("organization_units"); entity.Property(x => x.Name).HasMaxLength(200).IsRequired(); entity.Property(x => x.Code).HasMaxLength(64).IsRequired(); entity.HasIndex(x => x.Code).IsUnique(); entity.HasOne().WithMany().HasForeignKey(x => x.ParentId).OnDelete(DeleteBehavior.Restrict); }); modelBuilder.Entity(entity => { entity.ToTable("election_activities"); entity.Property(x => x.Name).HasMaxLength(200).IsRequired(); entity.Property(x => x.Description).HasMaxLength(4000); entity.HasIndex(x => new { x.RegionId, x.Status, x.VotingEndsAtUtc }); entity.HasOne().WithMany().HasForeignKey(x => x.RegionId).OnDelete(DeleteBehavior.Restrict); }); modelBuilder.Entity(entity => { entity.ToTable("election_contests"); entity.Property(x => x.Title).HasMaxLength(200).IsRequired(); entity.Property(x => x.RequiredSupportPercent).HasPrecision(5, 2); entity.HasIndex(x => new { x.ElectionActivityId, x.SortOrder }).IsUnique(); entity.HasOne().WithMany().HasForeignKey(x => x.ElectionActivityId).OnDelete(DeleteBehavior.Cascade); }); modelBuilder.Entity(entity => { entity.ToTable("election_options"); entity.Property(x => x.Label).HasMaxLength(200).IsRequired(); entity.HasIndex(x => new { x.ElectionContestId, x.SortOrder }).IsUnique(); entity.HasIndex(x => new { x.ElectionContestId, x.CandidateId }).IsUnique(); entity.HasOne().WithMany().HasForeignKey(x => x.ElectionContestId).OnDelete(DeleteBehavior.Cascade); entity.HasOne().WithMany().HasForeignKey(x => x.CandidateId).OnDelete(DeleteBehavior.Restrict); }); modelBuilder.Entity(entity => { entity.ToTable("election_counting_stations"); entity.HasKey(x => new { x.ElectionActivityId, x.CountingStationId }); entity.HasOne().WithMany().HasForeignKey(x => x.ElectionActivityId).OnDelete(DeleteBehavior.Cascade); entity.HasOne().WithMany().HasForeignKey(x => x.CountingStationId).OnDelete(DeleteBehavior.Restrict); }); modelBuilder.Entity(entity => { entity.ToTable("offline_counting_sessions"); entity.Property(x => x.CorrectionReason).HasMaxLength(1000); entity.HasIndex(x => new { x.ElectionActivityId, x.CountingStationId, x.Version }).IsUnique(); entity.HasIndex(x => new { x.ElectionActivityId, x.CountingStationId, x.Status }); entity.HasOne().WithMany().HasForeignKey(x => x.ElectionActivityId).OnDelete(DeleteBehavior.Restrict); entity.HasOne().WithMany().HasForeignKey(x => x.CountingStationId).OnDelete(DeleteBehavior.Restrict); entity.HasOne().WithMany().HasForeignKey(x => x.ReplacesSessionId).OnDelete(DeleteBehavior.Restrict); }); modelBuilder.Entity(entity => { entity.ToTable("offline_count_submissions"); entity.HasIndex(x => new { x.OfflineCountingSessionId, x.EnteredByUserId }).IsUnique(); entity.HasOne().WithMany().HasForeignKey(x => x.OfflineCountingSessionId).OnDelete(DeleteBehavior.Cascade); }); modelBuilder.Entity(entity => { entity.ToTable("offline_count_tallies"); entity.HasIndex(x => new { x.OfflineCountSubmissionId, x.ElectionContestId, x.ElectionOptionId }).IsUnique(); entity.HasOne().WithMany().HasForeignKey(x => x.OfflineCountSubmissionId).OnDelete(DeleteBehavior.Cascade); entity.HasOne().WithMany().HasForeignKey(x => x.ElectionContestId).OnDelete(DeleteBehavior.Restrict); entity.HasOne().WithMany().HasForeignKey(x => x.ElectionOptionId).OnDelete(DeleteBehavior.Restrict); }); modelBuilder.Entity(entity => { entity.ToTable("audit_entries"); entity.Property(x => x.Action).HasMaxLength(128).IsRequired(); entity.Property(x => x.ResourceType).HasMaxLength(128).IsRequired(); entity.Property(x => x.TraceId).HasMaxLength(128); entity.Property(x => x.ActorId).HasMaxLength(128); entity.Property(x => x.MetadataJson).HasColumnType("json"); entity.HasIndex(x => new { x.OccurredAtUtc, x.ResourceType }); }); } }