using Microsoft.EntityFrameworkCore; using SchotzIadKmTracker.Api.Models.Entities; namespace SchotzIadKmTracker.Api.Data; /// /// Contexte EF Core de l'application. Déclare les , les contraintes CHECK, /// les index et les index uniques conformément à la section « Schéma SQLite » de la conception. /// public class AppDbContext : DbContext { public AppDbContext(DbContextOptions options) : base(options) { } public DbSet Trajets => Set(); public DbSet Vehicules => Set(); public DbSet Profil => Set(); public DbSet ParametresApplication => Set(); public DbSet AdresseCache => Set(); public DbSet AdressesNommees => Set(); public DbSet TranchesBareme => Set(); public DbSet MontantsRegles => Set(); public DbSet Comptes => Set(); public DbSet ResetPasswordTokens => Set(); protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); // --- Trajets --- modelBuilder.Entity(entity => { entity.ToTable("Trajets", t => { // Distance nulle autorisée (Exigence 2.4 : distance non calculable) ; sinon strictement positive. t.HasCheckConstraint("CK_Trajets_Distance", "\"Distance\" IS NULL OR \"Distance\" > 0"); t.HasCheckConstraint("CK_Trajets_PuissanceFiscale", "\"PuissanceFiscale\" > 0"); }); entity.HasIndex(t => t.Date, "IX_Trajets_Date"); entity.HasIndex(t => new { t.Date, t.Position }, "IX_Trajets_Date_Position"); entity.HasIndex(t => t.VehiculeId, "IX_Trajets_Vehicule"); entity.HasOne(t => t.Vehicule) .WithMany(v => v.Trajets) .HasForeignKey(t => t.VehiculeId) .OnDelete(DeleteBehavior.Restrict); }); // --- Vehicules --- modelBuilder.Entity(entity => { entity.ToTable("Vehicules", t => t.HasCheckConstraint("CK_Vehicules_PuissanceFiscale", "\"PuissanceFiscale\" > 0")); entity.HasIndex(v => v.DateEffet, "IX_Vehicules_DateEffet"); }); // --- Profil (singleton Id = 1) --- modelBuilder.Entity(entity => { entity.ToTable("Profil"); entity.HasKey(p => p.Id); entity.Property(p => p.Id).ValueGeneratedNever(); entity.HasIndex(p => p.Id, "UX_Profil_Singleton").IsUnique(); }); // --- ParametresApplication (singleton Id = 1) --- modelBuilder.Entity(entity => { entity.ToTable("ParametresApplication", t => t.HasCheckConstraint("CK_ParametresApplication_PrecisionDistanceKm", "\"PrecisionDistanceKm\" IN (0, 1, 2)")); entity.HasKey(p => p.Id); entity.Property(p => p.Id).ValueGeneratedNever(); entity.HasIndex(p => p.Id, "UX_ParametresApplication_Singleton").IsUnique(); }); // --- AdresseCache (Adresse unique) --- modelBuilder.Entity(entity => { entity.ToTable("AdresseCache"); entity.HasIndex(a => a.Adresse).IsUnique(); }); // --- AdressesNommees (unicité sur DenominationNormalisee) --- modelBuilder.Entity(entity => { entity.ToTable("AdressesNommees"); entity.HasIndex(a => a.DenominationNormalisee, "UX_AdresseNommee_Denomination").IsUnique(); }); // --- TranchesBareme --- modelBuilder.Entity(entity => { // Convention [KmMin, KmMax[ : borne max EXCLUSIVE, ou NULL = illimité. // La contrainte tolère KmMax NULL et impose sinon KmMin < KmMax. // Convention [KmMin, KmMax[ : borne max EXCLUSIVE, ou NULL = illimité. // Les décimaux sont stockés en TEXT par SQLite : on CAST en REAL pour comparer // numériquement (sinon "5000" > "20000" en comparaison lexicographique de chaînes). entity.ToTable("TranchesBareme", t => t.HasCheckConstraint( "CK_TranchesBareme_Km", "\"KmMax\" IS NULL OR CAST(\"KmMin\" AS REAL) < CAST(\"KmMax\" AS REAL)")); entity.HasIndex(t => new { t.Annee, t.PuissanceFiscale }, "IX_TranchesBareme_Annee_PF"); }); // --- MontantsRegles --- modelBuilder.Entity(entity => { entity.ToTable("MontantsRegles", t => { t.HasCheckConstraint("CK_MontantsRegles_Montant", "\"Montant\" >= 0"); t.HasCheckConstraint("CK_MontantsRegles_Mois", "\"Mois\" BETWEEN 1 AND 12"); }); entity.HasIndex(m => new { m.Annee, m.Mois, m.VehiculeId }, "UX_MontantsRegles_Periode_Vehicule") .IsUnique(); entity.HasOne(m => m.Vehicule) .WithMany() .HasForeignKey(m => m.VehiculeId) .OnDelete(DeleteBehavior.Restrict); }); // --- Comptes (singleton Id = 1) --- modelBuilder.Entity(entity => { entity.ToTable("Comptes"); entity.HasKey(c => c.Id); entity.Property(c => c.Id).ValueGeneratedNever(); entity.HasIndex(c => c.Email, "UX_Comptes_Email").IsUnique(); }); // --- ResetPasswordTokens (jetons de réinitialisation à usage unique) --- modelBuilder.Entity(entity => { entity.ToTable("ResetPasswordTokens"); entity.HasIndex(t => t.TokenHash, "UX_ResetTokens_TokenHash").IsUnique(); entity.HasIndex(t => t.Expiration, "IX_ResetTokens_Expiration"); }); } }