perf(adminui): raw tree renders tag rows from a projection without TagConfig
This commit is contained in:
@@ -173,6 +173,7 @@ public sealed class RawTreeService(IDbContextFactory<OtOpcUaConfigDbContext> dbF
|
|||||||
var tags = await db.Tags.AsNoTracking()
|
var tags = await db.Tags.AsNoTracking()
|
||||||
.Where(t => t.DeviceId == deviceId && t.TagGroupId == null)
|
.Where(t => t.DeviceId == deviceId && t.TagGroupId == null)
|
||||||
.OrderBy(t => t.Name)
|
.OrderBy(t => t.Name)
|
||||||
|
.Select(t => new TagRow(t.TagId, t.Name, t.RowVersion))
|
||||||
.ToListAsync(ct);
|
.ToListAsync(ct);
|
||||||
|
|
||||||
var nodes = new List<RawNode>(groups.Count + tags.Count);
|
var nodes = new List<RawNode>(groups.Count + tags.Count);
|
||||||
@@ -194,6 +195,7 @@ public sealed class RawTreeService(IDbContextFactory<OtOpcUaConfigDbContext> dbF
|
|||||||
var tags = await db.Tags.AsNoTracking()
|
var tags = await db.Tags.AsNoTracking()
|
||||||
.Where(t => t.TagGroupId == groupId)
|
.Where(t => t.TagGroupId == groupId)
|
||||||
.OrderBy(t => t.Name)
|
.OrderBy(t => t.Name)
|
||||||
|
.Select(t => new TagRow(t.TagId, t.Name, t.RowVersion))
|
||||||
.ToListAsync(ct);
|
.ToListAsync(ct);
|
||||||
|
|
||||||
var nodes = new List<RawNode>(subGroups.Count + tags.Count);
|
var nodes = new List<RawNode>(subGroups.Count + tags.Count);
|
||||||
@@ -299,7 +301,13 @@ public sealed class RawTreeService(IDbContextFactory<OtOpcUaConfigDbContext> dbF
|
|||||||
}).ToList();
|
}).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static RawNode BuildTagNode(RawNode parent, Tag t) => new()
|
/// <summary>
|
||||||
|
/// The only columns a tree row needs from a <see cref="Tag" />. Projecting to this keeps the
|
||||||
|
/// <c>TagConfig</c> nvarchar(max) JSON blob out of the tree's SELECT — it is never rendered.
|
||||||
|
/// </summary>
|
||||||
|
private sealed record TagRow(string TagId, string Name, byte[] RowVersion);
|
||||||
|
|
||||||
|
private static RawNode BuildTagNode(RawNode parent, TagRow t) => new()
|
||||||
{
|
{
|
||||||
Kind = RawNodeKind.Tag,
|
Kind = RawNodeKind.Tag,
|
||||||
Key = $"tag:{t.TagId}",
|
Key = $"tag:{t.TagId}",
|
||||||
|
|||||||
@@ -102,6 +102,41 @@ public sealed class RawTreeServiceTests
|
|||||||
(await service.LoadChildrenAsync(tags[0])).ShouldBeEmpty(); // leaf
|
(await service.LoadChildrenAsync(tags[0])).ShouldBeEmpty(); // leaf
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tag rows are read through a narrow projection that leaves the <c>TagConfig</c> JSON blob in the
|
||||||
|
/// database, so this pins that the three fields a tree row actually renders — identity, name and the
|
||||||
|
/// concurrency token the mutation handles round-trip — still arrive intact, from both tag loaders
|
||||||
|
/// (a tag under a group, and a tag hanging directly off a device).
|
||||||
|
/// </summary>
|
||||||
|
[Fact]
|
||||||
|
public async Task LoadChildren_yields_tag_rows_carrying_id_name_and_row_version()
|
||||||
|
{
|
||||||
|
var (service, dbName) = Seeded();
|
||||||
|
var cluster = (await service.LoadRootsAsync()).Single().Children.Single();
|
||||||
|
|
||||||
|
// Group-child loader: two tags under "Motors".
|
||||||
|
var folder = (await service.LoadChildrenAsync(cluster)).Single(n => n.Kind == RawNodeKind.Folder);
|
||||||
|
var driver = (await service.LoadChildrenAsync(folder)).ShouldHaveSingleItem();
|
||||||
|
var device = (await service.LoadChildrenAsync(driver)).ShouldHaveSingleItem();
|
||||||
|
var group = (await service.LoadChildrenAsync(device)).ShouldHaveSingleItem();
|
||||||
|
|
||||||
|
var plain = (await service.LoadChildrenAsync(group)).Single(t => t.EntityId == RawTreeTestDb.PlainTagId);
|
||||||
|
plain.DisplayName.ShouldBe("state");
|
||||||
|
plain.RowVersion.ShouldBe(
|
||||||
|
RowVersionOf(dbName, db => db.Tags.Single(t => t.TagId == RawTreeTestDb.PlainTagId).RowVersion));
|
||||||
|
|
||||||
|
// Device-child loader: the cluster-root device's own tag.
|
||||||
|
var rootDriver = (await service.LoadChildrenAsync(cluster)).Single(n => n.Kind == RawNodeKind.Driver);
|
||||||
|
var rootDevice = (await service.LoadChildrenAsync(rootDriver)).ShouldHaveSingleItem();
|
||||||
|
|
||||||
|
var referenced = (await service.LoadChildrenAsync(rootDevice))
|
||||||
|
.Single(t => t.EntityId == RawTreeTestDb.ReferencedTagId);
|
||||||
|
referenced.Kind.ShouldBe(RawNodeKind.Tag);
|
||||||
|
referenced.DisplayName.ShouldBe("temperature");
|
||||||
|
referenced.RowVersion.ShouldBe(
|
||||||
|
RowVersionOf(dbName, db => db.Tags.Single(t => t.TagId == RawTreeTestDb.ReferencedTagId).RowVersion));
|
||||||
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------------- Create/happy
|
// -------------------------------------------------------------------------------- Create/happy
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|||||||
Reference in New Issue
Block a user