perf(adminui): /scripts lists a projection and loads source per dialog (R04 P8)
The list loaded every script entity including SourceCode to render names and
hashes. ScriptListQueries projects {ScriptId, Name, Language, SourceHash} for
the list and fetches one script's source when its Source dialog opens, with a
generation guard against a superseded open.
Refs #745
Claude-Session: https://claude.ai/code/session_01Ub9hGmYLQvn9XETzw7CMsT
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
@rendermode RenderMode.InteractiveServer
|
||||
@using Microsoft.EntityFrameworkCore
|
||||
@using ZB.MOM.WW.OtOpcUa.Configuration
|
||||
@using ZB.MOM.WW.OtOpcUa.Configuration.Entities
|
||||
@using ZB.MOM.WW.OtOpcUa.AdminUI.ScriptList
|
||||
@inject IDbContextFactory<OtOpcUaConfigDbContext> DbFactory
|
||||
|
||||
<PageErrorBoundary>
|
||||
@@ -89,13 +89,17 @@ else
|
||||
<button type="button" class="btn-close" aria-label="Close" @onclick="CloseSource"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
@if (src is not null)
|
||||
@if (src is null || (_sourceLoaded && _sourceCode is null))
|
||||
{
|
||||
<pre class="mono small mb-0" style="background:var(--zebra-bg);padding:1rem;border-radius:4px;overflow:auto">@src.SourceCode</pre>
|
||||
<p class="text-muted mb-0">This script is no longer loaded.</p>
|
||||
}
|
||||
else if (!_sourceLoaded)
|
||||
{
|
||||
<p class="text-muted mb-0">Loading source…</p>
|
||||
}
|
||||
else
|
||||
{
|
||||
<p class="text-muted mb-0">This script is no longer loaded.</p>
|
||||
<pre class="mono small mb-0" style="background:var(--zebra-bg);padding:1rem;border-radius:4px;overflow:auto">@_sourceCode</pre>
|
||||
}
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
@@ -108,30 +112,53 @@ else
|
||||
</PageErrorBoundary>
|
||||
|
||||
@code {
|
||||
private List<Script>? _rows;
|
||||
// A projection without SourceCode (R04 P8); the source loads per dialog open.
|
||||
private List<ScriptListRow>? _rows;
|
||||
|
||||
// Only the id of the script whose source dialog is open — never the record itself, so a reload
|
||||
// that drops or replaces the row cannot leave a stale object on screen.
|
||||
private string? _sourceScriptId;
|
||||
|
||||
// The open dialog's source, fetched on open. _sourceGeneration discards a superseded fetch (the
|
||||
// operator opened another row, or closed the dialog, while the first one was in flight).
|
||||
private string? _sourceCode;
|
||||
private bool _sourceLoaded;
|
||||
private int _sourceGeneration;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await using var db = await DbFactory.CreateDbContextAsync();
|
||||
_rows = await db.Scripts.AsNoTracking()
|
||||
.OrderBy(s => s.Name)
|
||||
.ToListAsync();
|
||||
_rows = await ScriptListQueries.LoadRowsAsync(db);
|
||||
}
|
||||
|
||||
/// <summary>Re-resolves the open dialog's script from the current list on every render. Drives
|
||||
/// the dialog's CONTENT only — never its existence (see the comment at the markup): a script
|
||||
/// that is no longer loaded renders a notice with the frame and Close still mounted.</summary>
|
||||
private Script? FindSourceScript()
|
||||
private ScriptListRow? FindSourceScript()
|
||||
=> _sourceScriptId is null
|
||||
? null
|
||||
: _rows?.FirstOrDefault(s => s.ScriptId == _sourceScriptId);
|
||||
|
||||
// One dialog at a time: opening another row replaces the id.
|
||||
private void ShowSource(string scriptId) => _sourceScriptId = scriptId;
|
||||
private async Task ShowSource(string scriptId)
|
||||
{
|
||||
var generation = ++_sourceGeneration;
|
||||
_sourceScriptId = scriptId;
|
||||
_sourceCode = null;
|
||||
_sourceLoaded = false;
|
||||
|
||||
private void CloseSource() => _sourceScriptId = null;
|
||||
await using var db = await DbFactory.CreateDbContextAsync();
|
||||
var source = await ScriptListQueries.LoadSourceAsync(db, scriptId);
|
||||
if (generation != _sourceGeneration) return;
|
||||
_sourceCode = source;
|
||||
_sourceLoaded = true;
|
||||
}
|
||||
|
||||
private void CloseSource()
|
||||
{
|
||||
_sourceGeneration++;
|
||||
_sourceScriptId = null;
|
||||
_sourceCode = null;
|
||||
_sourceLoaded = false;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user