using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; using ZB.MOM.WW.ScadaBridge.Commons.Entities.Deployment; using ZB.MOM.WW.ScadaBridge.Commons.Entities.Instances; namespace ZB.MOM.WW.ScadaBridge.ConfigurationDatabase.Configurations; public class DeploymentRecordConfiguration : IEntityTypeConfiguration { /// Configures the EF Core mapping for . /// The entity type builder. public void Configure(EntityTypeBuilder builder) { builder.HasKey(d => d.Id); builder.Property(d => d.DeploymentId) .IsRequired() .HasMaxLength(100); builder.Property(d => d.RevisionHash) .HasMaxLength(100); builder.Property(d => d.DeployedBy) .IsRequired() .HasMaxLength(200); builder.Property(d => d.Status) .HasConversion() .HasMaxLength(50); builder.HasOne() .WithMany() .HasForeignKey(d => d.InstanceId) .OnDelete(DeleteBehavior.Restrict); // Optimistic concurrency on deployment status records builder.Property("RowVersion") .IsRowVersion(); builder.HasIndex(d => d.DeploymentId).IsUnique(); builder.HasIndex(d => d.InstanceId); // Deployment-list sort key. Every list read orders DeployedAt DESC, Id DESC // (DeploymentManagerRepository.QueryDeploymentListPageAsync, which register // row 37 made the single paged read behind BOTH the Central UI page and the // CLI/HTTP QueryDeployments command) — Id is the deterministic tie-break, because // a bulk site deploy stamps many rows in the same DeployedAt instant and a // non-deterministic order would let a row repeat or vanish across pages. // Declaring both key columns descending lets a forward scan feed the paged // Skip/Take directly instead of the server sorting the whole filtered set per // page (deferred-work register row 35). // // This REPLACES the old single-column HasIndex(d => d.DeployedAt): that index // is strictly redundant here, not merely overlapping. DeployedAt leads both, so // any predicate the old one served the composite serves too (direction does not // affect seekability — SQL Server scans either way), and the leaf is the same // width, since Id is the clustered PK and was therefore already sitting in the // old index's leaf as the row locator. Keeping it would buy nothing and cost a // second index maintained on every insert and status transition. builder.HasIndex(d => new { d.DeployedAt, d.Id }) .IsDescending(true, true) .HasDatabaseName("IX_DeploymentRecords_DeployedAt_Id"); } } public class DeployedConfigSnapshotConfiguration : IEntityTypeConfiguration { /// Configures the EF Core mapping for . /// The entity type builder. public void Configure(EntityTypeBuilder builder) { builder.HasKey(s => s.Id); builder.Property(s => s.DeploymentId) .IsRequired() .HasMaxLength(100); builder.Property(s => s.RevisionHash) .IsRequired() .HasMaxLength(100); builder.Property(s => s.ConfigurationJson) .IsRequired(); builder.HasOne() .WithMany() .HasForeignKey(s => s.InstanceId) .OnDelete(DeleteBehavior.Cascade); builder.HasIndex(s => s.InstanceId).IsUnique(); builder.HasIndex(s => s.DeploymentId); } } public class SystemArtifactDeploymentRecordConfiguration : IEntityTypeConfiguration { /// Configures the EF Core mapping for . /// The entity type builder. public void Configure(EntityTypeBuilder builder) { builder.HasKey(d => d.Id); builder.Property(d => d.ArtifactType) .IsRequired() .HasMaxLength(100); builder.Property(d => d.DeployedBy) .IsRequired() .HasMaxLength(200); builder.Property(d => d.PerSiteStatus) .HasMaxLength(4000); builder.HasIndex(d => d.DeployedAt); } }