Audit & Soft delete
Interceptor central
Application/Library/Interceptors/AuditableAndSoftDeleteInterceptor.cs est branché sur ApplicationDbContext dans Startup.cs > AddInterceptors.
Il fait deux choses au moment de SaveChanges :
1. Soft delete
Pour toute entité implémentant ISoftDelete qui passe en EntityState.Deleted :
- Le state est forcé à
EntityState.Modified. IsDeleted = trueest posé.DeletedAt/DeletedBy(si présents) sont remplis.
Conséquence : les entités ne sont jamais physiquement supprimées (sauf IgnoreQueryFilters() + RemoveRange).
2. Audit (timestamps + user)
Pour toute entité implémentant ITimeStamped ou IAuditableEntity :
- À
Added:CreatedAt = DateTime.Now,CreatedBy = <user>(résolu viaIHttpContextAccessor). - À
Modified:UpdatedAt,UpdatedBy.
Filtre global EF
ApplicationDbContext.OnModelCreating configure (à confirmer) un global query filter pour exclure IsDeleted = true automatiquement sur les entités ISoftDelete.
Conséquence : tous les _db.Bookings.Where(...) ignorent silencieusement les soft-deleted. Pour les inclure :
_db.Bookings.IgnoreQueryFilters().Where(...)Hiérarchie des classes de base
BaseEntity
├─ BaseTimeStampedEntity (CreatedAt, UpdatedAt — ITimeStamped)
└─ BaseAuditableEntity (+ CreatedBy, UpdatedBy — IAuditableEntity)Les entités métier choisissent ce dont elles ont besoin et implémentent ISoftDelete quand pertinent.
AuditEntity (journal séparé)
Une entité AuditEntity stocke un journal d'audit séparé pour les changements importants. Géré par AuditEntityService et exposé dans Pages/AuditEntities/. À ne pas confondre avec BaseAuditableEntity qui ne fait que poser CreatedBy/UpdatedBy.
Pièges courants
- Soft delete cascade : EF ne fait PAS de cascade soft-delete sur les enfants. Si on supprime un parent, les enfants restent en base avec
IsDeleted = falsemais le parent est masqué — donc orphelins potentiellement visibles si l'enfant est requêté indépendamment. À vérifier au cas par cas. - Sort + filter : un
OrderBy(x => x.Children.Count())peut renvoyer un compte qui inclut les soft-deleted enfants (ou pas, selon la version EF). Toujours tester. - Perf : le filter ajoute
WHERE IsDeleted = 0à toutes les queries. Indexer surIsDeletedou sur des composites (IsDeleted, FK) si nécessaire.
Remplacements directs (hard delete)
Cas autorisés :
- Purge admin (compte guest sans booking, cf.
CleanGuestCustomers). - Tests : on hard-delete pour repartir propre.
⚠ Toute hard-delete doit être explicite et justifiée (commentaire dans le code).

