10b83d69c5
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
42 lines
2.2 KiB
C#
42 lines
2.2 KiB
C#
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);
|
|
}
|