perf(templates): member add/update loads only the chain + composition closure (ScadaBridge#66)
Attribute/alarm/script add (collision check) and update (parent-lock lookup) used GetAllTemplatesForAnalysisAsync, materialising every template, attribute, alarm and script in the library per edit. They now call the new ITemplateEngineRepository.GetTemplateAnalysisClosureAsync: the same untracked, body-free analysis projection, restricted to the template's inheritance chain plus the composition closure of every chain member - exactly what TemplateResolver and CollisionDetector walk (a composed module's own parent chain is never climbed, so it is not loaded). Adds use the edited template's closure, updates the parent's. The walk (Commons TemplateAnalysisClosure) loads one batch per closure depth, requests each id once, terminates on cycles and skips dangling edges. Evidence: - TemplateAnalysisClosureTests: for every template of a graph with chains, nested + diamond compositions, a derived module, cycles, dangling edges and colliding unrelated templates, ResolveAllMembers / DetectCollisions / FindMemberByCanonicalName answer identically from the closure and the whole graph (mutation-checked). - TemplateAnalysisClosureRepositoryTests (SQLite, recorded SQL): 6 templates / 12 attributes loaded instead of 47 / 94, in 4 queries (one per depth), untracked, script bodies never selected. - TemplateServiceTests: two new tests with the whole-graph load wired to throw (fail on the old service); every existing add/update test now runs against the closure via AnalysisClosureMock. ReconcileDescendantsAsync still loads the tracked full graph; not in scope. Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
This commit is contained in:
+13
@@ -56,6 +56,19 @@ public interface ITemplateEngineRepository
|
||||
/// <returns>A task that resolves to a read-only list of all templates without script bodies.</returns>
|
||||
Task<IReadOnlyList<Template>> GetAllTemplatesForAnalysisAsync(CancellationToken cancellationToken = default);
|
||||
/// <summary>
|
||||
/// Returns the same untracked, script-body-free projection as
|
||||
/// <see cref="GetAllTemplatesForAnalysisAsync"/>, but only for the templates an analysis walk
|
||||
/// rooted at <paramref name="templateId"/> can reach: its inheritance chain plus the
|
||||
/// composition closure of every chain member (see
|
||||
/// <see cref="ZB.MOM.WW.ScadaBridge.Commons.Types.Templates.TemplateAnalysisClosure"/>).
|
||||
/// Backs member add/update validation (Gitea #66), which previously loaded every template.
|
||||
/// Unknown ids yield an empty list.
|
||||
/// </summary>
|
||||
/// <param name="templateId">The template whose chain and composition closure to load.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to the closure templates without script bodies.</returns>
|
||||
Task<IReadOnlyList<Template>> GetTemplateAnalysisClosureAsync(int templateId, CancellationToken cancellationToken = default);
|
||||
/// <summary>
|
||||
/// Returns an untracked identity/edge projection of every template (no children), ordered by name.
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
|
||||
Reference in New Issue
Block a user