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:
+250
@@ -0,0 +1,250 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using ZB.MOM.WW.ScadaBridge.Commons.Entities.Deployment;
|
||||
using ZB.MOM.WW.ScadaBridge.Commons.Entities.Instances;
|
||||
using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Repositories;
|
||||
|
||||
namespace ZB.MOM.WW.ScadaBridge.ConfigurationDatabase.Repositories;
|
||||
|
||||
/// <summary>
|
||||
/// EF Core implementation of <see cref="IDeploymentManagerRepository"/> covering
|
||||
/// the deployment pipeline's persistence surface: <c>DeploymentRecord</c> CRUD
|
||||
/// (with optimistic concurrency via <c>DeploymentRecord.RowVersion</c>),
|
||||
/// <c>SystemArtifactDeploymentRecord</c> CRUD, <c>DeployedConfigSnapshot</c> CRUD,
|
||||
/// and a Restrict-FK-aware <see cref="DeleteInstanceAsync"/> that explicitly
|
||||
/// clears dependent deployment-record rows before removing an instance.
|
||||
/// </summary>
|
||||
public class DeploymentManagerRepository : IDeploymentManagerRepository
|
||||
{
|
||||
private readonly ScadaBridgeDbContext _dbContext;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the DeploymentManagerRepository class.
|
||||
/// </summary>
|
||||
/// <param name="dbContext">The database context for accessing deployment data.</param>
|
||||
public DeploymentManagerRepository(ScadaBridgeDbContext dbContext)
|
||||
{
|
||||
_dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext));
|
||||
}
|
||||
|
||||
// --- DeploymentRecord ---
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<DeploymentRecord?> GetDeploymentRecordByIdAsync(int id, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await _dbContext.DeploymentRecords.FindAsync([id], cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyList<DeploymentRecord>> GetAllDeploymentRecordsAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await _dbContext.DeploymentRecords
|
||||
.OrderByDescending(d => d.DeployedAt)
|
||||
.ToListAsync(cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyList<DeploymentRecord>> GetDeploymentsByInstanceIdAsync(int instanceId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await _dbContext.DeploymentRecords
|
||||
.Where(d => d.InstanceId == instanceId)
|
||||
.OrderByDescending(d => d.DeployedAt)
|
||||
.ToListAsync(cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<DeploymentRecord?> GetCurrentDeploymentStatusAsync(int instanceId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await _dbContext.DeploymentRecords
|
||||
.Where(d => d.InstanceId == instanceId)
|
||||
.OrderByDescending(d => d.DeployedAt)
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<DeploymentRecord?> GetDeploymentByDeploymentIdAsync(string deploymentId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await _dbContext.DeploymentRecords
|
||||
.FirstOrDefaultAsync(d => d.DeploymentId == deploymentId, cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task AddDeploymentRecordAsync(DeploymentRecord record, CancellationToken cancellationToken = default)
|
||||
{
|
||||
await _dbContext.DeploymentRecords.AddAsync(record, cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task UpdateDeploymentRecordAsync(DeploymentRecord record, CancellationToken cancellationToken = default)
|
||||
{
|
||||
_dbContext.DeploymentRecords.Update(record);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task DeleteDeploymentRecordAsync(int id, byte[] expectedRowVersion, CancellationToken cancellationToken = default)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(expectedRowVersion);
|
||||
|
||||
// CD-017: DeploymentRecord carries a SQL Server rowversion concurrency token.
|
||||
// The stub-attach delete path must seed EF's OriginalValues["RowVersion"] with
|
||||
// the caller's last-observed value so the generated SQL becomes
|
||||
// `DELETE ... WHERE Id = @id AND RowVersion = @prior`. Without this seeding a
|
||||
// concurrent edit is silently overwritten; with it, EF raises
|
||||
// DbUpdateConcurrencyException on SaveChangesAsync — the documented
|
||||
// optimistic-concurrency contract on deployment status records.
|
||||
var record = _dbContext.DeploymentRecords.Local.FirstOrDefault(d => d.Id == id);
|
||||
if (record != null)
|
||||
{
|
||||
var entry = _dbContext.Entry(record);
|
||||
entry.OriginalValues["RowVersion"] = expectedRowVersion;
|
||||
_dbContext.DeploymentRecords.Remove(record);
|
||||
}
|
||||
else
|
||||
{
|
||||
var stub = new DeploymentRecord("stub", "stub") { Id = id };
|
||||
_dbContext.DeploymentRecords.Attach(stub);
|
||||
var entry = _dbContext.Entry(stub);
|
||||
entry.OriginalValues["RowVersion"] = expectedRowVersion;
|
||||
_dbContext.DeploymentRecords.Remove(stub);
|
||||
}
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
// --- SystemArtifactDeploymentRecord ---
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<SystemArtifactDeploymentRecord?> GetSystemArtifactDeploymentByIdAsync(int id, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await _dbContext.SystemArtifactDeploymentRecords.FindAsync([id], cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyList<SystemArtifactDeploymentRecord>> GetAllSystemArtifactDeploymentsAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await _dbContext.SystemArtifactDeploymentRecords
|
||||
.OrderByDescending(d => d.DeployedAt)
|
||||
.ToListAsync(cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task AddSystemArtifactDeploymentAsync(SystemArtifactDeploymentRecord record, CancellationToken cancellationToken = default)
|
||||
{
|
||||
await _dbContext.SystemArtifactDeploymentRecords.AddAsync(record, cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task UpdateSystemArtifactDeploymentAsync(SystemArtifactDeploymentRecord record, CancellationToken cancellationToken = default)
|
||||
{
|
||||
_dbContext.SystemArtifactDeploymentRecords.Update(record);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task DeleteSystemArtifactDeploymentAsync(int id, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var record = _dbContext.SystemArtifactDeploymentRecords.Local.FirstOrDefault(d => d.Id == id);
|
||||
if (record != null)
|
||||
{
|
||||
_dbContext.SystemArtifactDeploymentRecords.Remove(record);
|
||||
}
|
||||
else
|
||||
{
|
||||
var stub = new SystemArtifactDeploymentRecord("stub", "stub") { Id = id };
|
||||
_dbContext.SystemArtifactDeploymentRecords.Attach(stub);
|
||||
_dbContext.SystemArtifactDeploymentRecords.Remove(stub);
|
||||
}
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
// --- WP-8: DeployedConfigSnapshot ---
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<DeployedConfigSnapshot?> GetDeployedSnapshotByInstanceIdAsync(int instanceId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await _dbContext.Set<DeployedConfigSnapshot>()
|
||||
.FirstOrDefaultAsync(s => s.InstanceId == instanceId, cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task AddDeployedSnapshotAsync(DeployedConfigSnapshot snapshot, CancellationToken cancellationToken = default)
|
||||
{
|
||||
await _dbContext.Set<DeployedConfigSnapshot>().AddAsync(snapshot, cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task UpdateDeployedSnapshotAsync(DeployedConfigSnapshot snapshot, CancellationToken cancellationToken = default)
|
||||
{
|
||||
_dbContext.Set<DeployedConfigSnapshot>().Update(snapshot);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task DeleteDeployedSnapshotAsync(int instanceId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var snapshot = await _dbContext.Set<DeployedConfigSnapshot>()
|
||||
.FirstOrDefaultAsync(s => s.InstanceId == instanceId, cancellationToken);
|
||||
if (snapshot != null)
|
||||
{
|
||||
_dbContext.Set<DeployedConfigSnapshot>().Remove(snapshot);
|
||||
}
|
||||
}
|
||||
|
||||
// --- Instance lookups for deployment pipeline ---
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<Instance?> GetInstanceByIdAsync(int instanceId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await _dbContext.Set<Instance>()
|
||||
.Include(i => i.AttributeOverrides)
|
||||
.Include(i => i.AlarmOverrides)
|
||||
.Include(i => i.ConnectionBindings)
|
||||
.AsSplitQuery()
|
||||
.FirstOrDefaultAsync(i => i.Id == instanceId, cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<Instance?> GetInstanceByUniqueNameAsync(string uniqueName, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await _dbContext.Set<Instance>()
|
||||
.Include(i => i.AttributeOverrides)
|
||||
.Include(i => i.AlarmOverrides)
|
||||
.Include(i => i.ConnectionBindings)
|
||||
.AsSplitQuery()
|
||||
.FirstOrDefaultAsync(i => i.UniqueName == uniqueName, cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task UpdateInstanceAsync(Instance instance, CancellationToken cancellationToken = default)
|
||||
{
|
||||
_dbContext.Set<Instance>().Update(instance);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task DeleteInstanceAsync(int instanceId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
// DeploymentRecords have a Restrict FK to Instance — remove them
|
||||
// explicitly first. The snapshot, overrides, and connection bindings
|
||||
// are configured with cascade delete and go with the instance.
|
||||
var records = await _dbContext.DeploymentRecords
|
||||
.Where(d => d.InstanceId == instanceId)
|
||||
.ToListAsync(cancellationToken);
|
||||
if (records.Count > 0)
|
||||
{
|
||||
_dbContext.DeploymentRecords.RemoveRange(records);
|
||||
}
|
||||
|
||||
var instance = await _dbContext.Set<Instance>()
|
||||
.FirstOrDefaultAsync(i => i.Id == instanceId, cancellationToken);
|
||||
if (instance != null)
|
||||
{
|
||||
_dbContext.Set<Instance>().Remove(instance);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await _dbContext.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user