#nullable enable using Baya.Application.Contracts.Common; using Baya.Domain.Common; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Diagnostics; namespace Baya.Infrastructure.Persistence.Interceptors; /// /// Stamps audit fields on every save: CreatedAt/CreatedById on insert and /// ModifiedAt/ModifiedById on update, sourcing time from /// and the acting user from . Handlers never set these fields. /// This is the extension point backend-phase-1 builds on to also write append-only audit-log rows. /// public sealed class AuditFieldInterceptor(ICurrentUser currentUser, IDateTimeProvider dateTimeProvider) : SaveChangesInterceptor { public override InterceptionResult SavingChanges( DbContextEventData eventData, InterceptionResult result) { Stamp(eventData.Context); return base.SavingChanges(eventData, result); } public override ValueTask> SavingChangesAsync( DbContextEventData eventData, InterceptionResult result, CancellationToken cancellationToken = default) { Stamp(eventData.Context); return base.SavingChangesAsync(eventData, result, cancellationToken); } private void Stamp(DbContext? context) { if (context is null) return; var now = dateTimeProvider.UtcNow; var userId = currentUser.UserId; foreach (var entry in context.ChangeTracker.Entries()) { switch (entry.State) { case EntityState.Added: entry.Entity.CreatedAt = now; entry.Entity.ModifiedAt = now; if (entry.Entity is IAuditableEntity addedAuditable) { addedAuditable.CreatedById = userId; addedAuditable.ModifiedById = userId; } break; case EntityState.Modified: entry.Entity.ModifiedAt = now; if (entry.Entity is IAuditableEntity modifiedAuditable) modifiedAuditable.ModifiedById = userId; break; } } } }