Files
ScadaBridge/src/ZB.MOM.WW.ScadaBridge.ConfigurationDatabase/Services/ConfigurationCommandTransaction.cs
T
Joseph Doherty 0ffd4bdbea docs(src): fix CommentChecker findings across src/ — 1,032 → 5 (all false positives)
Comment/XML-doc changes only, no code logic:
- strip project-tracking references (WP*, P*-*, row-NN, #NN, Task N) from
  comments, keeping the explanatory rationale
- add missing summaries, <param>, <returns>, <typeparam> and <inheritdoc/>
- replace misused <inheritdoc/> with real docs; where <inheritdoc/> was
  combined with useful remarks, move the rationale into // comments
- drop stale replicationActor <param> on DeploymentManagerActor ctor

Remaining 5 are false positives (CWE-1236 x2, failures-1, year-0001, M365).
Build 0 errors / 0 warnings.
2026-10-07 14:12:25 -04:00

59 lines
2.8 KiB
C#

using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Services;
namespace ZB.MOM.WW.ScadaBridge.ConfigurationDatabase.Services;
/// <summary>Owns the SQL transaction spanning configuration saves and the final audit flush.</summary>
public sealed class ConfigurationCommandTransaction(IServiceScopeFactory scopes) : IConfigurationCommandTransaction
{
/// <inheritdoc />
public async Task<object?> ExecuteAsync(Func<IServiceProvider, Task<object?>> operation)
{
using var strategyScope = scopes.CreateScope();
var strategy = strategyScope.ServiceProvider.GetRequiredService<ScadaBridgeDbContext>().Database.CreateExecutionStrategy();
var commitStarted = false;
var result = await strategy.ExecuteAsync(async () =>
{
// A lost commit acknowledgment must never automatically replay a create.
if (commitStarted) throw new InvalidOperationException("Configuration commit outcome is uncertain; inspect the configuration audit before retrying.");
await using var attempt = scopes.CreateAsyncScope();
var db = attempt.ServiceProvider.GetRequiredService<ScadaBridgeDbContext>();
await using var transaction = await db.Database.BeginTransactionAsync();
var value = await operation(attempt.ServiceProvider);
await db.SaveChangesAsync(); // AuditService stages rows; flush them inside this transaction.
commitStarted = true;
await transaction.CommitAsync();
await attempt.ServiceProvider.GetRequiredService<ConfigurationCommitActions>().RunAsync();
return value;
});
return result;
}
}
/// <summary>Per-attempt postcommit actions; rolled-back attempts never run them.</summary>
public sealed class ConfigurationCommitActions(ILogger<ConfigurationCommitActions> logger) : IConfigurationCommitActions
{
private readonly List<Func<Task>> _actions = [];
/// <inheritdoc />
public void Add(Func<Task> action) => _actions.Add(action);
/// <summary>Runs every registered post-commit action, in registration order.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
internal async Task RunAsync()
{
foreach (var action in _actions)
{
try { await action(); }
catch (Exception ex) { logger.LogError(ex, "Configuration committed, but local cache maintenance failed"); }
}
}
}
/// <summary>Refreshes circuit-held read entities after a command owned by another scope.</summary>
public sealed class ConfigurationReadSession(ScadaBridgeDbContext db) : IConfigurationReadSession
{
/// <inheritdoc />
public void Reset() => db.ChangeTracker.Clear();
}