refactor: rename ScadaLink → ZB.MOM.WW.ScadaBridge (code + projects + namespaces)
Solution + 23 src projects + 26 test projects renamed; folders, csproj, namespaces, and ScadaLinkDbContext/ScadaBridgeDbContext class updated. ActorSystem "scadalink" → "scadabridge", Akka seed-node URLs migrated. SQL roles/logins, LDAP domains, CLI command name, and CLI config dir (~/.scadalink → ~/.scadabridge) also renamed. Build green; 5 Host.Tests fail awaiting SQL login rename in next commit. Pre-existing StaleTagMonitor timing flakes unchanged. Rename script committed at tools/rename-to-scadabridge.sh.
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
using ZB.MOM.WW.ScadaBridge.Commons.Entities.Audit;
|
||||
|
||||
namespace ZB.MOM.WW.ScadaBridge.ConfigurationDatabase.Configurations;
|
||||
|
||||
public class AuditLogEntryConfiguration : IEntityTypeConfiguration<AuditLogEntry>
|
||||
{
|
||||
/// <summary>
|
||||
/// Configures the EF Core entity mapping for <see cref="AuditLogEntry"/>.
|
||||
/// </summary>
|
||||
/// <param name="builder">The entity type builder for <see cref="AuditLogEntry"/>.</param>
|
||||
public void Configure(EntityTypeBuilder<AuditLogEntry> builder)
|
||||
{
|
||||
builder.HasKey(a => a.Id);
|
||||
|
||||
builder.Property(a => a.User)
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
builder.Property(a => a.Action)
|
||||
.IsRequired()
|
||||
.HasMaxLength(100);
|
||||
|
||||
builder.Property(a => a.EntityType)
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
builder.Property(a => a.EntityId)
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
builder.Property(a => a.EntityName)
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
// Indexes for common query patterns
|
||||
builder.HasIndex(a => a.Timestamp);
|
||||
builder.HasIndex(a => a.User);
|
||||
builder.HasIndex(a => a.EntityType);
|
||||
builder.HasIndex(a => a.EntityId);
|
||||
builder.HasIndex(a => a.Action);
|
||||
builder.HasIndex(a => a.BundleImportId).HasDatabaseName("IX_AuditLogEntries_BundleImportId");
|
||||
}
|
||||
}
|
||||
+155
@@ -0,0 +1,155 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using ZB.MOM.WW.ScadaBridge.Commons.Entities.Audit;
|
||||
|
||||
namespace ZB.MOM.WW.ScadaBridge.ConfigurationDatabase.Configurations;
|
||||
|
||||
/// <summary>
|
||||
/// Maps the <see cref="AuditEvent"/> record to the central <c>AuditLog</c> table
|
||||
/// described in alog.md §4. Column lengths/types and the five named indexes are
|
||||
/// fixed by that specification — keep this in sync with the doc.
|
||||
/// </summary>
|
||||
public class AuditLogEntityTypeConfiguration : IEntityTypeConfiguration<AuditEvent>
|
||||
{
|
||||
// SQL Server's datetime2 provider strips the DateTimeKind flag on the wire
|
||||
// (a column hydrated from the database always surfaces as
|
||||
// DateTimeKind.Unspecified). Without a converter, downstream code that
|
||||
// calls .ToLocalTime() / .ToUniversalTime() on an OccurredAtUtc value would
|
||||
// silently re-interpret it as local time. These converters force the Kind
|
||||
// back to Utc on read, and re-stamp Utc on write so a producer that hands
|
||||
// EF a DateTime literal with Kind=Unspecified still lands a UTC-tagged
|
||||
// value in the model cache (CLAUDE.md: "All timestamps are UTC throughout
|
||||
// the system."). Applied to every DateTime property whose name ends in
|
||||
// `Utc`; DateTimeOffset columns already carry their own offset and are NOT
|
||||
// routed through these converters.
|
||||
private static readonly ValueConverter<DateTime, DateTime> UtcConverter = new(
|
||||
v => v.Kind == DateTimeKind.Utc ? v : DateTime.SpecifyKind(v, DateTimeKind.Utc),
|
||||
v => DateTime.SpecifyKind(v, DateTimeKind.Utc));
|
||||
|
||||
private static readonly ValueConverter<DateTime?, DateTime?> NullableUtcConverter = new(
|
||||
v => v.HasValue
|
||||
? (v.Value.Kind == DateTimeKind.Utc ? v.Value : DateTime.SpecifyKind(v.Value, DateTimeKind.Utc))
|
||||
: null,
|
||||
v => v.HasValue ? DateTime.SpecifyKind(v.Value, DateTimeKind.Utc) : null);
|
||||
|
||||
/// <summary>Applies the EF Core type configuration for <see cref="AuditEvent"/> to the model builder.</summary>
|
||||
/// <param name="builder">The entity type builder to configure.</param>
|
||||
public void Configure(EntityTypeBuilder<AuditEvent> builder)
|
||||
{
|
||||
builder.ToTable("AuditLog");
|
||||
|
||||
// Enforce DateTimeKind.Utc on every *Utc-suffixed DateTime column. See
|
||||
// the UtcConverter remarks above for the rationale.
|
||||
builder.Property(e => e.OccurredAtUtc).HasConversion(UtcConverter);
|
||||
builder.Property(e => e.IngestedAtUtc).HasConversion(NullableUtcConverter);
|
||||
|
||||
// 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)
|
||||
.HasConversion<string>()
|
||||
.HasMaxLength(32)
|
||||
.IsUnicode(false)
|
||||
.IsRequired();
|
||||
|
||||
builder.Property(e => e.Kind)
|
||||
.HasConversion<string>()
|
||||
.HasMaxLength(32)
|
||||
.IsUnicode(false)
|
||||
.IsRequired();
|
||||
|
||||
builder.Property(e => e.Status)
|
||||
.HasConversion<string>()
|
||||
.HasMaxLength(32)
|
||||
.IsUnicode(false)
|
||||
.IsRequired();
|
||||
|
||||
builder.Property(e => e.ForwardState)
|
||||
.HasConversion<string>()
|
||||
.HasMaxLength(32)
|
||||
.IsUnicode(false);
|
||||
|
||||
// Ascii identifier columns — never carry user-supplied unicode.
|
||||
builder.Property(e => e.SourceSiteId)
|
||||
.HasMaxLength(64)
|
||||
.IsUnicode(false);
|
||||
|
||||
builder.Property(e => e.SourceInstanceId)
|
||||
.HasMaxLength(128)
|
||||
.IsUnicode(false);
|
||||
|
||||
builder.Property(e => e.SourceScript)
|
||||
.HasMaxLength(128)
|
||||
.IsUnicode(false);
|
||||
|
||||
builder.Property(e => e.Actor)
|
||||
.HasMaxLength(128)
|
||||
.IsUnicode(false);
|
||||
|
||||
builder.Property(e => e.Target)
|
||||
.HasMaxLength(256)
|
||||
.IsUnicode(false);
|
||||
|
||||
// SourceNode (Audit Log #23, SourceNode-stamping): node-local identifier of the
|
||||
// cluster member that produced the row (e.g. "node-a", "central-a"). NULL is
|
||||
// valid for reconciled rows from a retired node and for direct-write rows
|
||||
// produced before this feature shipped. ASCII — varchar(64), no unicode.
|
||||
builder.Property(e => e.SourceNode)
|
||||
.HasColumnType("varchar(64)")
|
||||
.HasMaxLength(64)
|
||||
.IsUnicode(false);
|
||||
|
||||
// Bounded unicode message column.
|
||||
builder.Property(e => e.ErrorMessage)
|
||||
.HasMaxLength(1024);
|
||||
|
||||
// ErrorDetail, RequestSummary, ResponseSummary, Extra: leave as nvarchar(max).
|
||||
|
||||
// Indexes — names locked to alog.md §4 for reconciliation/migration discoverability.
|
||||
builder.HasIndex(e => e.OccurredAtUtc)
|
||||
.IsDescending(true)
|
||||
.HasDatabaseName("IX_AuditLog_OccurredAtUtc");
|
||||
|
||||
builder.HasIndex(e => new { e.SourceSiteId, e.OccurredAtUtc })
|
||||
.IsDescending(false, true)
|
||||
.HasDatabaseName("IX_AuditLog_Site_Occurred");
|
||||
|
||||
builder.HasIndex(e => e.CorrelationId)
|
||||
.HasFilter("[CorrelationId] IS NOT NULL")
|
||||
.HasDatabaseName("IX_AuditLog_CorrelationId");
|
||||
|
||||
builder.HasIndex(e => e.ExecutionId)
|
||||
.HasFilter("[ExecutionId] IS NOT NULL")
|
||||
.HasDatabaseName("IX_AuditLog_Execution");
|
||||
|
||||
builder.HasIndex(e => e.ParentExecutionId)
|
||||
.HasFilter("[ParentExecutionId] IS NOT NULL")
|
||||
.HasDatabaseName("IX_AuditLog_ParentExecution");
|
||||
|
||||
// SourceNode composite index (Audit Log #23, SourceNode-stamping): backs
|
||||
// per-node Central UI / health-dashboard queries (e.g. "rows produced by
|
||||
// central-a, newest first"). Created via raw SQL in the migration so it lands
|
||||
// on the ps_AuditLog_Month(OccurredAtUtc) partition scheme like every other
|
||||
// IX_AuditLog_* index — keeps the partition-switch purge path intact.
|
||||
builder.HasIndex(e => new { e.SourceNode, e.OccurredAtUtc })
|
||||
.HasDatabaseName("IX_AuditLog_Node_Occurred");
|
||||
|
||||
builder.HasIndex(e => new { e.Channel, e.Status, e.OccurredAtUtc })
|
||||
.IsDescending(false, false, true)
|
||||
.HasDatabaseName("IX_AuditLog_Channel_Status_Occurred");
|
||||
|
||||
builder.HasIndex(e => new { e.Target, e.OccurredAtUtc })
|
||||
.IsDescending(false, true)
|
||||
.HasFilter("[Target] IS NOT NULL")
|
||||
.HasDatabaseName("IX_AuditLog_Target_Occurred");
|
||||
}
|
||||
}
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
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);
|
||||
builder.HasIndex(d => d.DeployedAt);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
using ZB.MOM.WW.ScadaBridge.Commons.Entities.ExternalSystems;
|
||||
|
||||
namespace ZB.MOM.WW.ScadaBridge.ConfigurationDatabase.Configurations;
|
||||
|
||||
public class ExternalSystemDefinitionConfiguration : IEntityTypeConfiguration<ExternalSystemDefinition>
|
||||
{
|
||||
/// <summary>Applies the EF Core entity type configuration for <see cref="ExternalSystemDefinition"/>.</summary>
|
||||
/// <param name="builder">The entity type builder to configure.</param>
|
||||
public void Configure(EntityTypeBuilder<ExternalSystemDefinition> builder)
|
||||
{
|
||||
builder.HasKey(e => e.Id);
|
||||
|
||||
builder.Property(e => e.Name)
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
builder.Property(e => e.EndpointUrl)
|
||||
.IsRequired()
|
||||
.HasMaxLength(2000);
|
||||
|
||||
builder.Property(e => e.AuthType)
|
||||
.IsRequired()
|
||||
.HasMaxLength(50);
|
||||
|
||||
// Stored encrypted at rest (EncryptedStringConverter). Ciphertext is larger than
|
||||
// the plaintext, so the column is sized generously to avoid truncation.
|
||||
builder.Property(e => e.AuthConfiguration)
|
||||
.HasMaxLength(8000);
|
||||
|
||||
builder.HasMany<ExternalSystemMethod>()
|
||||
.WithOne()
|
||||
.HasForeignKey(m => m.ExternalSystemDefinitionId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
builder.HasIndex(e => e.Name).IsUnique();
|
||||
}
|
||||
}
|
||||
|
||||
public class ExternalSystemMethodConfiguration : IEntityTypeConfiguration<ExternalSystemMethod>
|
||||
{
|
||||
/// <summary>Applies the EF Core entity type configuration for <see cref="ExternalSystemMethod"/>.</summary>
|
||||
/// <param name="builder">The entity type builder to configure.</param>
|
||||
public void Configure(EntityTypeBuilder<ExternalSystemMethod> builder)
|
||||
{
|
||||
builder.HasKey(m => m.Id);
|
||||
|
||||
builder.Property(m => m.Name)
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
builder.Property(m => m.HttpMethod)
|
||||
.IsRequired()
|
||||
.HasMaxLength(10);
|
||||
|
||||
builder.Property(m => m.Path)
|
||||
.IsRequired()
|
||||
.HasMaxLength(2000);
|
||||
|
||||
builder.Property(m => m.ParameterDefinitions)
|
||||
.HasMaxLength(4000);
|
||||
|
||||
builder.Property(m => m.ReturnDefinition)
|
||||
.HasMaxLength(4000);
|
||||
|
||||
builder.HasIndex(m => new { m.ExternalSystemDefinitionId, m.Name }).IsUnique();
|
||||
}
|
||||
}
|
||||
|
||||
public class DatabaseConnectionDefinitionConfiguration : IEntityTypeConfiguration<DatabaseConnectionDefinition>
|
||||
{
|
||||
/// <summary>Applies the EF Core entity type configuration for <see cref="DatabaseConnectionDefinition"/>.</summary>
|
||||
/// <param name="builder">The entity type builder to configure.</param>
|
||||
public void Configure(EntityTypeBuilder<DatabaseConnectionDefinition> builder)
|
||||
{
|
||||
builder.HasKey(d => d.Id);
|
||||
|
||||
builder.Property(d => d.Name)
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
// Stored encrypted at rest (EncryptedStringConverter). Ciphertext is larger than
|
||||
// the plaintext, so the column is sized generously to avoid truncation.
|
||||
builder.Property(d => d.ConnectionString)
|
||||
.IsRequired()
|
||||
.HasMaxLength(8000);
|
||||
|
||||
builder.HasIndex(d => d.Name).IsUnique();
|
||||
}
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
using ZB.MOM.WW.ScadaBridge.Commons.Entities.InboundApi;
|
||||
|
||||
namespace ZB.MOM.WW.ScadaBridge.ConfigurationDatabase.Configurations;
|
||||
|
||||
public class ApiKeyConfiguration : IEntityTypeConfiguration<ApiKey>
|
||||
{
|
||||
/// <summary>Configures the EF Core mapping for the <see cref="ApiKey"/> entity.</summary>
|
||||
/// <param name="builder">Entity type builder used to apply the configuration.</param>
|
||||
public void Configure(EntityTypeBuilder<ApiKey> builder)
|
||||
{
|
||||
builder.HasKey(k => k.Id);
|
||||
|
||||
builder.Property(k => k.Name)
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
// ConfigurationDatabase-012: the bearer credential is persisted only as a
|
||||
// deterministic HMAC-SHA256 hash, never as plaintext. Base64 of a 32-byte
|
||||
// HMAC-SHA256 digest is 44 characters; 256 leaves generous headroom.
|
||||
builder.Property(k => k.KeyHash)
|
||||
.IsRequired()
|
||||
.HasMaxLength(256);
|
||||
|
||||
builder.HasIndex(k => k.Name).IsUnique();
|
||||
builder.HasIndex(k => k.KeyHash).IsUnique();
|
||||
}
|
||||
}
|
||||
|
||||
public class ApiMethodConfiguration : IEntityTypeConfiguration<ApiMethod>
|
||||
{
|
||||
/// <summary>Configures the EF Core mapping for the <see cref="ApiMethod"/> entity.</summary>
|
||||
/// <param name="builder">Entity type builder used to apply the configuration.</param>
|
||||
public void Configure(EntityTypeBuilder<ApiMethod> builder)
|
||||
{
|
||||
builder.HasKey(m => m.Id);
|
||||
|
||||
builder.Property(m => m.Name)
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
builder.Property(m => m.Script)
|
||||
.IsRequired();
|
||||
|
||||
builder.Property(m => m.ApprovedApiKeyIds)
|
||||
.HasMaxLength(4000);
|
||||
|
||||
builder.Property(m => m.ParameterDefinitions)
|
||||
.HasMaxLength(4000);
|
||||
|
||||
builder.Property(m => m.ReturnDefinition)
|
||||
.HasMaxLength(4000);
|
||||
|
||||
builder.HasIndex(m => m.Name).IsUnique();
|
||||
}
|
||||
}
|
||||
+145
@@ -0,0 +1,145 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
using ZB.MOM.WW.ScadaBridge.Commons.Entities.Instances;
|
||||
using ZB.MOM.WW.ScadaBridge.Commons.Entities.Sites;
|
||||
using ZB.MOM.WW.ScadaBridge.Commons.Entities.Templates;
|
||||
|
||||
namespace ZB.MOM.WW.ScadaBridge.ConfigurationDatabase.Configurations;
|
||||
|
||||
public class InstanceConfiguration : IEntityTypeConfiguration<Instance>
|
||||
{
|
||||
/// <summary>Configures the EF Core mapping for <see cref="Instance"/>.</summary>
|
||||
/// <param name="builder">The entity type builder.</param>
|
||||
public void Configure(EntityTypeBuilder<Instance> builder)
|
||||
{
|
||||
builder.HasKey(i => i.Id);
|
||||
|
||||
builder.Property(i => i.UniqueName)
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
builder.Property(i => i.State)
|
||||
.HasConversion<string>()
|
||||
.HasMaxLength(50);
|
||||
|
||||
builder.HasOne<Template>()
|
||||
.WithMany()
|
||||
.HasForeignKey(i => i.TemplateId)
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
|
||||
builder.HasOne<Site>()
|
||||
.WithMany()
|
||||
.HasForeignKey(i => i.SiteId)
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
|
||||
builder.HasOne<Area>()
|
||||
.WithMany()
|
||||
.HasForeignKey(i => i.AreaId)
|
||||
.OnDelete(DeleteBehavior.SetNull)
|
||||
.IsRequired(false);
|
||||
|
||||
builder.HasMany(i => i.AttributeOverrides)
|
||||
.WithOne()
|
||||
.HasForeignKey(o => o.InstanceId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
builder.HasMany(i => i.AlarmOverrides)
|
||||
.WithOne()
|
||||
.HasForeignKey(o => o.InstanceId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
builder.HasMany(i => i.ConnectionBindings)
|
||||
.WithOne()
|
||||
.HasForeignKey(b => b.InstanceId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
builder.HasIndex(i => new { i.SiteId, i.UniqueName }).IsUnique();
|
||||
}
|
||||
}
|
||||
|
||||
public class InstanceAttributeOverrideConfiguration : IEntityTypeConfiguration<InstanceAttributeOverride>
|
||||
{
|
||||
/// <summary>Configures the EF Core mapping for <see cref="InstanceAttributeOverride"/>.</summary>
|
||||
/// <param name="builder">The entity type builder.</param>
|
||||
public void Configure(EntityTypeBuilder<InstanceAttributeOverride> builder)
|
||||
{
|
||||
builder.HasKey(o => o.Id);
|
||||
|
||||
builder.Property(o => o.AttributeName)
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
builder.Property(o => o.OverrideValue)
|
||||
.HasMaxLength(4000);
|
||||
|
||||
builder.HasIndex(o => new { o.InstanceId, o.AttributeName }).IsUnique();
|
||||
}
|
||||
}
|
||||
|
||||
public class InstanceAlarmOverrideConfiguration : IEntityTypeConfiguration<InstanceAlarmOverride>
|
||||
{
|
||||
/// <summary>Configures the EF Core mapping for <see cref="InstanceAlarmOverride"/>.</summary>
|
||||
/// <param name="builder">The entity type builder.</param>
|
||||
public void Configure(EntityTypeBuilder<InstanceAlarmOverride> builder)
|
||||
{
|
||||
builder.HasKey(o => o.Id);
|
||||
|
||||
builder.Property(o => o.AlarmCanonicalName)
|
||||
.IsRequired()
|
||||
.HasMaxLength(400); // Larger than attribute names to fit composed paths.
|
||||
|
||||
builder.Property(o => o.TriggerConfigurationOverride)
|
||||
.HasMaxLength(4000);
|
||||
|
||||
builder.HasIndex(o => new { o.InstanceId, o.AlarmCanonicalName }).IsUnique();
|
||||
}
|
||||
}
|
||||
|
||||
public class InstanceConnectionBindingConfiguration : IEntityTypeConfiguration<InstanceConnectionBinding>
|
||||
{
|
||||
/// <summary>Configures the EF Core mapping for <see cref="InstanceConnectionBinding"/>.</summary>
|
||||
/// <param name="builder">The entity type builder.</param>
|
||||
public void Configure(EntityTypeBuilder<InstanceConnectionBinding> builder)
|
||||
{
|
||||
builder.HasKey(b => b.Id);
|
||||
|
||||
builder.Property(b => b.AttributeName)
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
builder.HasOne<DataConnection>()
|
||||
.WithMany()
|
||||
.HasForeignKey(b => b.DataConnectionId)
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
|
||||
builder.HasIndex(b => new { b.InstanceId, b.AttributeName }).IsUnique();
|
||||
}
|
||||
}
|
||||
|
||||
public class AreaConfiguration : IEntityTypeConfiguration<Area>
|
||||
{
|
||||
/// <summary>Configures the EF Core mapping for <see cref="Area"/>.</summary>
|
||||
/// <param name="builder">The entity type builder.</param>
|
||||
public void Configure(EntityTypeBuilder<Area> builder)
|
||||
{
|
||||
builder.HasKey(a => a.Id);
|
||||
|
||||
builder.Property(a => a.Name)
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
builder.HasOne<Site>()
|
||||
.WithMany()
|
||||
.HasForeignKey(a => a.SiteId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
// Self-referencing parent area
|
||||
builder.HasOne<Area>()
|
||||
.WithMany(a => a.Children)
|
||||
.HasForeignKey(a => a.ParentAreaId)
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired(false);
|
||||
|
||||
builder.HasIndex(a => new { a.SiteId, a.ParentAreaId, a.Name }).IsUnique();
|
||||
}
|
||||
}
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
using ZB.MOM.WW.ScadaBridge.Commons.Entities.Notifications;
|
||||
|
||||
namespace ZB.MOM.WW.ScadaBridge.ConfigurationDatabase.Configurations;
|
||||
|
||||
public class NotificationListConfiguration : IEntityTypeConfiguration<NotificationList>
|
||||
{
|
||||
/// <summary>Configures the EF Core mapping for <see cref="NotificationList"/>.</summary>
|
||||
/// <param name="builder">The entity type builder.</param>
|
||||
public void Configure(EntityTypeBuilder<NotificationList> builder)
|
||||
{
|
||||
builder.HasKey(n => n.Id);
|
||||
|
||||
builder.Property(n => n.Name)
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
builder.Property(n => n.Type)
|
||||
.HasConversion<string>()
|
||||
.HasMaxLength(32)
|
||||
.IsRequired();
|
||||
|
||||
builder.HasMany(n => n.Recipients)
|
||||
.WithOne()
|
||||
.HasForeignKey(r => r.NotificationListId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
builder.HasIndex(n => n.Name).IsUnique();
|
||||
}
|
||||
}
|
||||
|
||||
public class NotificationRecipientConfiguration : IEntityTypeConfiguration<NotificationRecipient>
|
||||
{
|
||||
/// <summary>Configures the EF Core mapping for <see cref="NotificationRecipient"/>.</summary>
|
||||
/// <param name="builder">The entity type builder.</param>
|
||||
public void Configure(EntityTypeBuilder<NotificationRecipient> builder)
|
||||
{
|
||||
builder.HasKey(r => r.Id);
|
||||
|
||||
builder.Property(r => r.Name)
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
builder.Property(r => r.EmailAddress)
|
||||
.IsRequired()
|
||||
.HasMaxLength(500);
|
||||
}
|
||||
}
|
||||
|
||||
public class SmtpConfigurationConfiguration : IEntityTypeConfiguration<SmtpConfiguration>
|
||||
{
|
||||
/// <summary>Configures the EF Core mapping for <see cref="SmtpConfiguration"/>.</summary>
|
||||
/// <param name="builder">The entity type builder.</param>
|
||||
public void Configure(EntityTypeBuilder<SmtpConfiguration> builder)
|
||||
{
|
||||
builder.HasKey(s => s.Id);
|
||||
|
||||
builder.Property(s => s.Host)
|
||||
.IsRequired()
|
||||
.HasMaxLength(500);
|
||||
|
||||
builder.Property(s => s.AuthType)
|
||||
.IsRequired()
|
||||
.HasMaxLength(50);
|
||||
|
||||
// Stored encrypted at rest (EncryptedStringConverter). Ciphertext is larger than
|
||||
// the plaintext, so the column is sized generously to avoid truncation.
|
||||
builder.Property(s => s.Credentials)
|
||||
.HasMaxLength(8000);
|
||||
|
||||
builder.Property(s => s.TlsMode)
|
||||
.HasMaxLength(50);
|
||||
|
||||
builder.Property(s => s.FromAddress)
|
||||
.IsRequired()
|
||||
.HasMaxLength(500);
|
||||
}
|
||||
}
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
using ZB.MOM.WW.ScadaBridge.Commons.Entities.Notifications;
|
||||
|
||||
namespace ZB.MOM.WW.ScadaBridge.ConfigurationDatabase.Configurations;
|
||||
|
||||
/// <summary>
|
||||
/// EF Core mapping for the central notification outbox entity. <see cref="Notification.TypeData"/>
|
||||
/// and <see cref="Notification.ResolvedTargets"/> are intentionally left unconstrained
|
||||
/// (nullable nvarchar(max)) as they carry variable-length JSON / target snapshots.
|
||||
/// </summary>
|
||||
public class NotificationOutboxConfiguration : IEntityTypeConfiguration<Notification>
|
||||
{
|
||||
/// <summary>Configures the EF Core entity type mapping for <see cref="Notification"/>.</summary>
|
||||
/// <param name="builder">The entity type builder to configure.</param>
|
||||
public void Configure(EntityTypeBuilder<Notification> builder)
|
||||
{
|
||||
builder.HasKey(n => n.NotificationId);
|
||||
|
||||
builder.Property(n => n.NotificationId).HasMaxLength(64);
|
||||
|
||||
builder.Property(n => n.Type)
|
||||
.HasConversion<string>()
|
||||
.HasMaxLength(32)
|
||||
.IsRequired();
|
||||
|
||||
builder.Property(n => n.Status)
|
||||
.HasConversion<string>()
|
||||
.HasMaxLength(32)
|
||||
.IsRequired();
|
||||
|
||||
builder.Property(n => n.ListName)
|
||||
.HasMaxLength(200)
|
||||
.IsRequired();
|
||||
|
||||
builder.Property(n => n.Subject)
|
||||
.HasMaxLength(1000)
|
||||
.IsRequired();
|
||||
|
||||
builder.Property(n => n.Body).IsRequired();
|
||||
|
||||
builder.Property(n => n.LastError).HasMaxLength(4000);
|
||||
|
||||
builder.Property(n => n.SourceSiteId)
|
||||
.HasMaxLength(100)
|
||||
.IsRequired();
|
||||
|
||||
builder.Property(n => n.SourceInstanceId).HasMaxLength(200);
|
||||
|
||||
builder.Property(n => n.SourceScript).HasMaxLength(200);
|
||||
|
||||
// SourceNode (Audit Log #23, SourceNode-stamping): node-local identifier of the
|
||||
// cluster member that produced the notification (e.g. "node-a", "central-a").
|
||||
// NULL is valid for rows that pre-date this feature. ASCII — varchar(64).
|
||||
// No index — KPIs are per-site on this table, not per-node; SourceNode is only
|
||||
// echoed onto NotifyDeliver audit rows (#23) for cross-row correlation.
|
||||
builder.Property(n => n.SourceNode)
|
||||
.HasColumnType("varchar(64)")
|
||||
.HasMaxLength(64)
|
||||
.IsUnicode(false);
|
||||
|
||||
// OriginExecutionId (Audit Log #23): nullable uniqueidentifier carried from the
|
||||
// site so the dispatcher can echo it onto NotifyDeliver audit rows. No index —
|
||||
// it is never a query predicate on this table, only copied onto audit events.
|
||||
|
||||
// OriginParentExecutionId (Audit Log #23): nullable uniqueidentifier carried from
|
||||
// the site — the routed run's parent ExecutionId — so the dispatcher can echo it
|
||||
// onto NotifyDeliver audit rows. No index — same rationale as OriginExecutionId.
|
||||
|
||||
builder.HasIndex(n => new { n.Status, n.NextAttemptAt });
|
||||
|
||||
builder.HasIndex(n => new { n.SourceSiteId, n.CreatedAt });
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
using ZB.MOM.WW.ScadaBridge.Commons.Entities.Scripts;
|
||||
|
||||
namespace ZB.MOM.WW.ScadaBridge.ConfigurationDatabase.Configurations;
|
||||
|
||||
public class SharedScriptConfiguration : IEntityTypeConfiguration<SharedScript>
|
||||
{
|
||||
/// <summary>Configures the EF Core mapping for the <see cref="SharedScript"/> entity.</summary>
|
||||
/// <param name="builder">Entity type builder used to apply the configuration.</param>
|
||||
public void Configure(EntityTypeBuilder<SharedScript> builder)
|
||||
{
|
||||
builder.HasKey(s => s.Id);
|
||||
|
||||
builder.Property(s => s.Name)
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
builder.Property(s => s.Code)
|
||||
.IsRequired();
|
||||
|
||||
builder.Property(s => s.ParameterDefinitions)
|
||||
.HasMaxLength(4000);
|
||||
|
||||
builder.Property(s => s.ReturnDefinition)
|
||||
.HasMaxLength(4000);
|
||||
|
||||
builder.HasIndex(s => s.Name).IsUnique();
|
||||
}
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
using ZB.MOM.WW.ScadaBridge.Commons.Entities.Security;
|
||||
using ZB.MOM.WW.ScadaBridge.Commons.Entities.Sites;
|
||||
|
||||
namespace ZB.MOM.WW.ScadaBridge.ConfigurationDatabase.Configurations;
|
||||
|
||||
public class LdapGroupMappingConfiguration : IEntityTypeConfiguration<LdapGroupMapping>
|
||||
{
|
||||
/// <summary>
|
||||
/// Configures the EF Core entity type mapping for <see cref="LdapGroupMapping"/>.
|
||||
/// </summary>
|
||||
/// <param name="builder">The entity type builder to configure.</param>
|
||||
public void Configure(EntityTypeBuilder<LdapGroupMapping> builder)
|
||||
{
|
||||
builder.HasKey(m => m.Id);
|
||||
|
||||
builder.Property(m => m.LdapGroupName)
|
||||
.IsRequired()
|
||||
.HasMaxLength(500);
|
||||
|
||||
builder.Property(m => m.Role)
|
||||
.IsRequired()
|
||||
.HasMaxLength(100);
|
||||
|
||||
builder.HasIndex(m => m.LdapGroupName).IsUnique();
|
||||
|
||||
// Seed default group mappings matching GLAuth test users
|
||||
builder.HasData(
|
||||
new LdapGroupMapping("SCADA-Admins", "Admin") { Id = 1 },
|
||||
new LdapGroupMapping("SCADA-Designers", "Design") { Id = 2 },
|
||||
new LdapGroupMapping("SCADA-Deploy-All", "Deployment") { Id = 3 },
|
||||
new LdapGroupMapping("SCADA-Deploy-SiteA", "Deployment") { Id = 4 });
|
||||
}
|
||||
}
|
||||
|
||||
public class SiteScopeRuleConfiguration : IEntityTypeConfiguration<SiteScopeRule>
|
||||
{
|
||||
/// <summary>
|
||||
/// Configures the EF Core entity type mapping for <see cref="SiteScopeRule"/>.
|
||||
/// </summary>
|
||||
/// <param name="builder">The entity type builder to configure.</param>
|
||||
public void Configure(EntityTypeBuilder<SiteScopeRule> builder)
|
||||
{
|
||||
builder.HasKey(r => r.Id);
|
||||
|
||||
builder.HasOne<LdapGroupMapping>()
|
||||
.WithMany()
|
||||
.HasForeignKey(r => r.LdapGroupMappingId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
builder.HasOne<Site>()
|
||||
.WithMany()
|
||||
.HasForeignKey(r => r.SiteId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
builder.HasIndex(r => new { r.LdapGroupMappingId, r.SiteId }).IsUnique();
|
||||
}
|
||||
}
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
using ZB.MOM.WW.ScadaBridge.Commons.Entities.Audit;
|
||||
using ZB.MOM.WW.ScadaBridge.Commons.Types;
|
||||
|
||||
namespace ZB.MOM.WW.ScadaBridge.ConfigurationDatabase.Configurations;
|
||||
|
||||
/// <summary>
|
||||
/// Maps the <see cref="SiteCall"/> record to the central <c>SiteCalls</c> table
|
||||
/// (Site Call Audit #22, Audit Log #23 M3 Bundle B). Operational state — NOT audit —
|
||||
/// so the table is non-partitioned, standard <c>[PRIMARY]</c> filegroup, no DB-role
|
||||
/// restriction. Two named indexes back the Central UI's "from this site" and
|
||||
/// "in this status" queries.
|
||||
/// </summary>
|
||||
public class SiteCallEntityTypeConfiguration : IEntityTypeConfiguration<SiteCall>
|
||||
{
|
||||
/// <summary>
|
||||
/// Configures the EF Core entity type mapping for <see cref="SiteCall"/>.
|
||||
/// </summary>
|
||||
/// <param name="builder">The entity type builder to configure.</param>
|
||||
public void Configure(EntityTypeBuilder<SiteCall> builder)
|
||||
{
|
||||
builder.ToTable("SiteCalls");
|
||||
|
||||
// PK is the strong-typed TrackedOperationId. Stored as varchar(36) by converting
|
||||
// through the canonical "D"-format GUID string. Going through the string surface
|
||||
// (rather than uniqueidentifier) keeps the column shape identical to how the id
|
||||
// is serialised on the wire (gRPC strings, SQLite TEXT on the site) — one
|
||||
// consistent format everywhere makes operational debugging far easier than
|
||||
// mixing a uniqueidentifier central column with TEXT site columns.
|
||||
builder.HasKey(s => s.TrackedOperationId);
|
||||
|
||||
builder.Property(s => s.TrackedOperationId)
|
||||
.HasConversion(
|
||||
id => id.Value.ToString("D"),
|
||||
s => new TrackedOperationId(Guid.Parse(s)))
|
||||
.HasMaxLength(36)
|
||||
.IsUnicode(false)
|
||||
.IsRequired();
|
||||
|
||||
// Enum-as-string columns: bounded varchar, ASCII.
|
||||
builder.Property(s => s.Channel)
|
||||
.HasMaxLength(32)
|
||||
.IsUnicode(false)
|
||||
.IsRequired();
|
||||
|
||||
builder.Property(s => s.Status)
|
||||
.HasMaxLength(32)
|
||||
.IsUnicode(false)
|
||||
.IsRequired();
|
||||
|
||||
builder.Property(s => s.SourceSite)
|
||||
.HasMaxLength(64)
|
||||
.IsUnicode(false)
|
||||
.IsRequired();
|
||||
|
||||
builder.Property(s => s.Target)
|
||||
.HasMaxLength(256)
|
||||
.IsUnicode(false)
|
||||
.IsRequired();
|
||||
|
||||
// Bounded unicode message column.
|
||||
builder.Property(s => s.LastError)
|
||||
.HasMaxLength(1024);
|
||||
|
||||
// SourceNode (Audit Log #23, SourceNode-stamping): node-local identifier of the
|
||||
// cluster member that produced the call (e.g. "node-a", "central-a"). NULL is
|
||||
// valid for rows that pre-date this feature and for reconciled rows from a
|
||||
// retired node. ASCII — varchar(64). No index — Site Call Audit KPIs are
|
||||
// per-site, not per-node, on this table.
|
||||
builder.Property(s => s.SourceNode)
|
||||
.HasColumnType("varchar(64)")
|
||||
.HasMaxLength(64)
|
||||
.IsUnicode(false);
|
||||
|
||||
// Indexes — names locked for reconciliation/migration discoverability.
|
||||
// Source_Created backs "calls from this site" (Central UI Site Calls page,
|
||||
// filter by SourceSite, newest first).
|
||||
builder.HasIndex(s => new { s.SourceSite, s.CreatedAtUtc })
|
||||
.IsDescending(false, true)
|
||||
.HasDatabaseName("IX_SiteCalls_Source_Created");
|
||||
|
||||
// Status_Updated backs "calls in this status" (e.g. parked rows awaiting
|
||||
// operator action, newest UpdatedAtUtc first).
|
||||
builder.HasIndex(s => new { s.Status, s.UpdatedAtUtc })
|
||||
.IsDescending(false, true)
|
||||
.HasDatabaseName("IX_SiteCalls_Status_Updated");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
using ZB.MOM.WW.ScadaBridge.Commons.Entities.Sites;
|
||||
|
||||
namespace ZB.MOM.WW.ScadaBridge.ConfigurationDatabase.Configurations;
|
||||
|
||||
public class SiteConfiguration : IEntityTypeConfiguration<Site>
|
||||
{
|
||||
/// <summary>Configures the EF Core mapping for <see cref="Site"/>.</summary>
|
||||
/// <param name="builder">The entity type builder.</param>
|
||||
public void Configure(EntityTypeBuilder<Site> builder)
|
||||
{
|
||||
builder.HasKey(s => s.Id);
|
||||
|
||||
builder.Property(s => s.Name)
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
builder.Property(s => s.SiteIdentifier)
|
||||
.IsRequired()
|
||||
.HasMaxLength(100);
|
||||
|
||||
builder.Property(s => s.Description)
|
||||
.HasMaxLength(2000);
|
||||
|
||||
builder.Property(s => s.NodeAAddress).HasMaxLength(500);
|
||||
builder.Property(s => s.NodeBAddress).HasMaxLength(500);
|
||||
builder.Property(s => s.GrpcNodeAAddress).HasMaxLength(500);
|
||||
builder.Property(s => s.GrpcNodeBAddress).HasMaxLength(500);
|
||||
|
||||
builder.HasIndex(s => s.Name).IsUnique();
|
||||
builder.HasIndex(s => s.SiteIdentifier).IsUnique();
|
||||
}
|
||||
}
|
||||
|
||||
public class DataConnectionConfiguration : IEntityTypeConfiguration<DataConnection>
|
||||
{
|
||||
/// <summary>Configures the EF Core mapping for <see cref="DataConnection"/>.</summary>
|
||||
/// <param name="builder">The entity type builder.</param>
|
||||
public void Configure(EntityTypeBuilder<DataConnection> builder)
|
||||
{
|
||||
builder.HasKey(d => d.Id);
|
||||
|
||||
builder.Property(d => d.Name)
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
builder.Property(d => d.Protocol)
|
||||
.IsRequired()
|
||||
.HasMaxLength(50);
|
||||
|
||||
builder.Property(d => d.PrimaryConfiguration)
|
||||
.HasMaxLength(4000);
|
||||
|
||||
builder.Property(d => d.BackupConfiguration)
|
||||
.HasMaxLength(4000);
|
||||
|
||||
builder.Property(d => d.FailoverRetryCount)
|
||||
.IsRequired()
|
||||
.HasDefaultValue(3);
|
||||
|
||||
builder.HasOne<Site>()
|
||||
.WithMany()
|
||||
.HasForeignKey(d => d.SiteId)
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
|
||||
builder.HasIndex(d => new { d.SiteId, d.Name }).IsUnique();
|
||||
}
|
||||
}
|
||||
+193
@@ -0,0 +1,193 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
using ZB.MOM.WW.ScadaBridge.Commons.Entities.Templates;
|
||||
|
||||
namespace ZB.MOM.WW.ScadaBridge.ConfigurationDatabase.Configurations;
|
||||
|
||||
public class TemplateConfiguration : IEntityTypeConfiguration<Template>
|
||||
{
|
||||
/// <summary>Configures the EF Core mapping for <see cref="Template"/>.</summary>
|
||||
/// <param name="builder">The entity type builder.</param>
|
||||
public void Configure(EntityTypeBuilder<Template> builder)
|
||||
{
|
||||
builder.HasKey(t => t.Id);
|
||||
|
||||
builder.Property(t => t.Name)
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
builder.Property(t => t.Description)
|
||||
.HasMaxLength(2000);
|
||||
|
||||
// Only base (user-authored) templates are globally unique by name.
|
||||
// Derived templates store their *contained* name (the composition slot's
|
||||
// InstanceName), unique only within the owner — enforced by the
|
||||
// (TemplateId, InstanceName) index on TemplateComposition — so they are
|
||||
// excluded from this index via a filter.
|
||||
builder.HasIndex(t => t.Name).IsUnique().HasFilter("[IsDerived] = 0");
|
||||
|
||||
// Self-referencing parent template (inheritance)
|
||||
builder.HasOne<Template>()
|
||||
.WithMany()
|
||||
.HasForeignKey(t => t.ParentTemplateId)
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired(false);
|
||||
|
||||
builder.HasOne<TemplateFolder>()
|
||||
.WithMany()
|
||||
.HasForeignKey(t => t.FolderId)
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired(false);
|
||||
|
||||
builder.HasMany(t => t.Attributes)
|
||||
.WithOne()
|
||||
.HasForeignKey(a => a.TemplateId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
builder.HasMany(t => t.Alarms)
|
||||
.WithOne()
|
||||
.HasForeignKey(a => a.TemplateId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
builder.HasMany(t => t.Scripts)
|
||||
.WithOne()
|
||||
.HasForeignKey(s => s.TemplateId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
builder.HasMany(t => t.Compositions)
|
||||
.WithOne()
|
||||
.HasForeignKey(c => c.TemplateId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
}
|
||||
}
|
||||
|
||||
public class TemplateAttributeConfiguration : IEntityTypeConfiguration<TemplateAttribute>
|
||||
{
|
||||
/// <summary>Configures the EF Core mapping for <see cref="TemplateAttribute"/>.</summary>
|
||||
/// <param name="builder">The entity type builder.</param>
|
||||
public void Configure(EntityTypeBuilder<TemplateAttribute> builder)
|
||||
{
|
||||
builder.HasKey(a => a.Id);
|
||||
|
||||
builder.Property(a => a.Name)
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
builder.Property(a => a.Value)
|
||||
.HasMaxLength(4000);
|
||||
|
||||
builder.Property(a => a.Description)
|
||||
.HasMaxLength(2000);
|
||||
|
||||
builder.Property(a => a.DataSourceReference)
|
||||
.HasMaxLength(500);
|
||||
|
||||
builder.Property(a => a.DataType)
|
||||
.HasConversion<string>()
|
||||
.HasMaxLength(50);
|
||||
|
||||
builder.HasIndex(a => new { a.TemplateId, a.Name }).IsUnique();
|
||||
}
|
||||
}
|
||||
|
||||
public class TemplateAlarmConfiguration : IEntityTypeConfiguration<TemplateAlarm>
|
||||
{
|
||||
/// <summary>Configures the EF Core mapping for <see cref="TemplateAlarm"/>.</summary>
|
||||
/// <param name="builder">The entity type builder.</param>
|
||||
public void Configure(EntityTypeBuilder<TemplateAlarm> builder)
|
||||
{
|
||||
builder.HasKey(a => a.Id);
|
||||
|
||||
builder.Property(a => a.Name)
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
builder.Property(a => a.Description)
|
||||
.HasMaxLength(2000);
|
||||
|
||||
builder.Property(a => a.TriggerType)
|
||||
.HasConversion<string>()
|
||||
.HasMaxLength(50);
|
||||
|
||||
builder.Property(a => a.TriggerConfiguration)
|
||||
.HasMaxLength(4000);
|
||||
|
||||
builder.HasIndex(a => new { a.TemplateId, a.Name }).IsUnique();
|
||||
}
|
||||
}
|
||||
|
||||
public class TemplateScriptConfiguration : IEntityTypeConfiguration<TemplateScript>
|
||||
{
|
||||
/// <summary>Configures the EF Core mapping for <see cref="TemplateScript"/>.</summary>
|
||||
/// <param name="builder">The entity type builder.</param>
|
||||
public void Configure(EntityTypeBuilder<TemplateScript> builder)
|
||||
{
|
||||
builder.HasKey(s => s.Id);
|
||||
|
||||
builder.Property(s => s.Name)
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
builder.Property(s => s.Code)
|
||||
.IsRequired();
|
||||
|
||||
builder.Property(s => s.TriggerType)
|
||||
.HasMaxLength(50);
|
||||
|
||||
builder.Property(s => s.TriggerConfiguration)
|
||||
.HasMaxLength(4000);
|
||||
|
||||
builder.Property(s => s.ParameterDefinitions)
|
||||
.HasMaxLength(4000);
|
||||
|
||||
builder.Property(s => s.ReturnDefinition)
|
||||
.HasMaxLength(4000);
|
||||
|
||||
builder.HasIndex(s => new { s.TemplateId, s.Name }).IsUnique();
|
||||
}
|
||||
}
|
||||
|
||||
public class TemplateCompositionConfiguration : IEntityTypeConfiguration<TemplateComposition>
|
||||
{
|
||||
/// <summary>Configures the EF Core mapping for <see cref="TemplateComposition"/>.</summary>
|
||||
/// <param name="builder">The entity type builder.</param>
|
||||
public void Configure(EntityTypeBuilder<TemplateComposition> builder)
|
||||
{
|
||||
builder.HasKey(c => c.Id);
|
||||
|
||||
builder.Property(c => c.InstanceName)
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
// The composed template reference
|
||||
builder.HasOne<Template>()
|
||||
.WithMany()
|
||||
.HasForeignKey(c => c.ComposedTemplateId)
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
|
||||
builder.HasIndex(c => new { c.TemplateId, c.InstanceName }).IsUnique();
|
||||
}
|
||||
}
|
||||
|
||||
public class TemplateFolderConfiguration : IEntityTypeConfiguration<TemplateFolder>
|
||||
{
|
||||
/// <summary>Configures the EF Core mapping for <see cref="TemplateFolder"/>.</summary>
|
||||
/// <param name="builder">The entity type builder.</param>
|
||||
public void Configure(EntityTypeBuilder<TemplateFolder> builder)
|
||||
{
|
||||
builder.HasKey(f => f.Id);
|
||||
|
||||
builder.Property(f => f.Name)
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
builder.HasOne<TemplateFolder>()
|
||||
.WithMany()
|
||||
.HasForeignKey(f => f.ParentFolderId)
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired(false);
|
||||
|
||||
// Unique sibling name (case-insensitive enforced at service layer; this index is for fast lookup)
|
||||
builder.HasIndex(f => new { f.ParentFolderId, f.Name }).IsUnique();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user