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:
@@ -0,0 +1,41 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using ZB.MOM.WW.OtOpcUa.Configuration;
|
||||
|
||||
namespace ZB.MOM.WW.OtOpcUa.AdminUI.ScriptList;
|
||||
|
||||
/// <summary>One row of the <c>/scripts</c> list: everything the list renders, and nothing else.</summary>
|
||||
/// <param name="ScriptId">The script's logical id.</param>
|
||||
/// <param name="Name">The authored display name.</param>
|
||||
/// <param name="Language">The script language (e.g. <c>CSharp</c>).</param>
|
||||
/// <param name="SourceHash">The source hash shown abbreviated on the row.</param>
|
||||
public sealed record ScriptListRow(string ScriptId, string Name, string Language, string SourceHash);
|
||||
|
||||
/// <summary>
|
||||
/// Queries behind the <c>/scripts</c> page. The list is a projection that leaves out
|
||||
/// <c>SourceCode</c> (R04 P8): loading every script's full source to render names and hashes made
|
||||
/// the page cost proportional to the total size of every script in the fleet. The source is fetched
|
||||
/// on demand, for one script, when its "Source" dialog opens.
|
||||
/// </summary>
|
||||
public static class ScriptListQueries
|
||||
{
|
||||
/// <summary>Loads the list rows, ordered by name, without any script's source.</summary>
|
||||
/// <param name="db">The config DB context.</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>The list rows.</returns>
|
||||
public static Task<List<ScriptListRow>> LoadRowsAsync(OtOpcUaConfigDbContext db, CancellationToken ct = default) =>
|
||||
db.Scripts.AsNoTracking()
|
||||
.OrderBy(s => s.Name)
|
||||
.Select(s => new ScriptListRow(s.ScriptId, s.Name, s.Language, s.SourceHash))
|
||||
.ToListAsync(ct);
|
||||
|
||||
/// <summary>Loads one script's source, or null when the script no longer exists.</summary>
|
||||
/// <param name="db">The config DB context.</param>
|
||||
/// <param name="scriptId">The script's logical id.</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>The source code, or null.</returns>
|
||||
public static Task<string?> LoadSourceAsync(OtOpcUaConfigDbContext db, string scriptId, CancellationToken ct = default) =>
|
||||
db.Scripts.AsNoTracking()
|
||||
.Where(s => s.ScriptId == scriptId)
|
||||
.Select(s => (string?)s.SourceCode)
|
||||
.FirstOrDefaultAsync(ct);
|
||||
}
|
||||
Reference in New Issue
Block a user