using Microsoft.Extensions.DependencyInjection; using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Repositories; namespace ZB.MOM.WW.ScadaBridge.CentralUI.Services; /// /// Default implementation (M9-T32c). Reads the /// named JSON-Schema library directly from over a /// fresh DI scope per query — mirroring AuditLogQueryService / /// KpiHistoryQueryService so a value form's auto-load never races other reads on /// the shared circuit-scoped DbContext. Read-only; no mutation goes through here. /// public sealed class SchemaLibraryQueryService : ISchemaLibraryQueryService { private readonly IServiceScopeFactory _scopeFactory; /// /// Initializes a new instance of the . /// /// Factory used to open a fresh DI scope (and DbContext) per query. public SchemaLibraryQueryService(IServiceScopeFactory scopeFactory) { _scopeFactory = scopeFactory ?? throw new ArgumentNullException(nameof(scopeFactory)); } /// public async Task> GetSchemaMapAsync( CancellationToken cancellationToken = default) { await using var scope = _scopeFactory.CreateAsyncScope(); var repo = scope.ServiceProvider.GetRequiredService(); var all = await repo.ListAsync(cancellationToken); // Ordinal-keyed to match the lib:Name resolver's exact-name lookup. Name is // DB-unique, so a list yields at most one row per name and no real collision // occurs; the indexer assignment is defensive only — should two rows ever share // a name (e.g. a mid-write transient read), the later one in enumeration order // overwrites the earlier rather than throwing. var map = new Dictionary(StringComparer.Ordinal); foreach (var schema in all) { map[schema.Name] = schema.SchemaJson; } return map; } /// public async Task ResolveAsync(string name, CancellationToken cancellationToken = default) { if (string.IsNullOrEmpty(name)) { return null; } await using var scope = _scopeFactory.CreateAsyncScope(); var repo = scope.ServiceProvider.GetRequiredService(); var schema = await repo.GetByNameAsync(name, cancellationToken); return schema?.SchemaJson; } }