0ffd4bdbea
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.
59 lines
2.8 KiB
C#
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();
|
|
}
|