47f3a703bf
Deployments free-text search and the config-audit EntityName filter no longer emit LIKE '%term%' by default. Prefix (LIKE 'term%') is the default; substring is an explicit opt-in (DeploymentSearchMode.Contains / entityNameContains) always bounded by a DeployedAt/Timestamp window (explicit, else 30 days). Migration PrefixSearchIndexes adds DeployedBy, RevisionHash and EntityName indexes. Deployments page gains a 'Match anywhere' checkbox. Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
122 lines
4.9 KiB
C#
122 lines
4.9 KiB
C#
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<DeploymentRecord>
|
|
{
|
|
/// <summary>Configures the EF Core mapping for <see cref="DeploymentRecord"/>.</summary>
|
|
/// <param name="builder">The entity type builder.</param>
|
|
public void Configure(EntityTypeBuilder<DeploymentRecord> 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<string>()
|
|
.HasMaxLength(50);
|
|
|
|
builder.HasOne<Instance>()
|
|
.WithMany()
|
|
.HasForeignKey(d => d.InstanceId)
|
|
.OnDelete(DeleteBehavior.Restrict);
|
|
|
|
// Optimistic concurrency on deployment status records
|
|
builder.Property<byte[]>("RowVersion")
|
|
.IsRowVersion();
|
|
|
|
builder.HasIndex(d => d.DeploymentId).IsUnique();
|
|
builder.HasIndex(d => d.InstanceId);
|
|
|
|
// Prefix-search legs of the Deployments page free-text search (ScadaBridge#50):
|
|
// DeploymentId and Instance.UniqueName are already uniquely indexed.
|
|
builder.HasIndex(d => d.DeployedBy).HasDatabaseName("IX_DeploymentRecords_DeployedBy");
|
|
builder.HasIndex(d => d.RevisionHash).HasDatabaseName("IX_DeploymentRecords_RevisionHash");
|
|
|
|
// 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<DeployedConfigSnapshot>
|
|
{
|
|
/// <summary>Configures the EF Core mapping for <see cref="DeployedConfigSnapshot"/>.</summary>
|
|
/// <param name="builder">The entity type builder.</param>
|
|
public void Configure(EntityTypeBuilder<DeployedConfigSnapshot> 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<Instance>()
|
|
.WithMany()
|
|
.HasForeignKey(s => s.InstanceId)
|
|
.OnDelete(DeleteBehavior.Cascade);
|
|
|
|
builder.HasIndex(s => s.InstanceId).IsUnique();
|
|
builder.HasIndex(s => s.DeploymentId);
|
|
}
|
|
}
|
|
|
|
public class SystemArtifactDeploymentRecordConfiguration : IEntityTypeConfiguration<SystemArtifactDeploymentRecord>
|
|
{
|
|
/// <summary>Configures the EF Core mapping for <see cref="SystemArtifactDeploymentRecord"/>.</summary>
|
|
/// <param name="builder">The entity type builder.</param>
|
|
public void Configure(EntityTypeBuilder<SystemArtifactDeploymentRecord> 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);
|
|
}
|
|
}
|