feat(configdb): composite PK + UX_AuditLog_EventId on AuditEvent mapping (#23)

Bundle C migration aligns AuditLog to ps_AuditLog_Month(OccurredAtUtc).
A partitioned table's clustered key must include the partition column, so
the PK becomes the composite {EventId, OccurredAtUtc} — a divergence from
Bundle B's single-column PK that needs reconciling in the EF mapping
before the migration is generated.

EventId remains the idempotency key for AuditLogRepository.InsertIfNotExistsAsync
(M1-T8), so a dedicated unique index UX_AuditLog_EventId preserves the
single-column uniqueness constraint.

Tests updated:
- Configure_MapsToAuditLogTable_WithCompositePrimaryKey (replaces the
  WithEventIdAsPrimaryKey assertion) verifies {EventId, OccurredAtUtc}.
- Configure_DeclaresUniqueIndex_OnEventIdAlone_ForIdempotencyLookups
  asserts the new UX_AuditLog_EventId is unique and on EventId alone.
- Configure_ExpectedIndexes_WithCorrectNames now expects six index names
  (the original five plus UX_AuditLog_EventId).
This commit is contained in:
Joseph Doherty
2026-05-20 10:19:33 -04:00
parent fb423b11ab
commit 7d9550f779
2 changed files with 39 additions and 5 deletions

View File

@@ -15,7 +15,15 @@ public class AuditLogEntityTypeConfiguration : IEntityTypeConfiguration<AuditEve
{
builder.ToTable("AuditLog");
builder.HasKey(e => e.EventId);
// Composite PK includes OccurredAtUtc — required by the monthly partition scheme
// (ps_AuditLog_Month) so the clustered key is partition-aligned. EventId still
// needs to be globally unique for InsertIfNotExistsAsync idempotency, so a
// separate unique index is declared on EventId alone.
builder.HasKey(e => new { e.EventId, e.OccurredAtUtc });
builder.HasIndex(e => e.EventId)
.IsUnique()
.HasDatabaseName("UX_AuditLog_EventId");
// Enum-as-string columns: bounded varchar(32) ASCII.
builder.Property(e => e.Channel)