perf(adminui): one guard round-trip per bulk reference add

This commit is contained in:
Joseph Doherty
2026-08-16 04:03:49 -04:00
parent 541a223b96
commit fa36d64e8f
5 changed files with 251 additions and 28 deletions
@@ -58,4 +58,38 @@ public interface IEffectiveNameGuard
EffectiveNameSourceKind kind,
string? excludeSourceId,
CancellationToken ct = default);
/// <summary>
/// Checks many proposed names against ONE load of the equipment's name space. Returns the first
/// collision message (in input order), or <see langword="null"/> when all are free.
/// </summary>
/// <remarks>
/// The bulk-authoring entry point (a select-all "add references" over N raw tags), where the
/// per-name <see cref="CheckAsync"/> would cost N contexts × 4 queries. There is no
/// <c>excludeSourceId</c>: a batch is a CREATE of N new rows, so no self-row exists to exclude.
/// The default implementation simply loops <see cref="CheckAsync"/> — correct but not fast — so
/// a stub / no-op guard needs no change; <c>EffectiveNameGuard</c> overrides it with the
/// single-load version.
/// </remarks>
/// <param name="equipmentId">The owning equipment's logical id.</param>
/// <param name="proposedEffectiveNames">The effective names to be authored, in the order the caller wants them reported.</param>
/// <param name="kind">Which surface the proposed names are for (words the error; identifies the self-row kind).</param>
/// <param name="ct">Cancellation token.</param>
/// <returns><see langword="null"/> when every name is free; otherwise the first name's readable error.</returns>
async Task<string?> CheckManyAsync(
string equipmentId,
IReadOnlyList<string> proposedEffectiveNames,
EffectiveNameSourceKind kind,
CancellationToken ct = default)
{
ArgumentNullException.ThrowIfNull(proposedEffectiveNames);
foreach (var proposed in proposedEffectiveNames)
{
var collision = await CheckAsync(equipmentId, proposed, kind, excludeSourceId: null, ct);
if (collision is not null) return collision;
}
return null;
}
}