using Microsoft.EntityFrameworkCore; using ZB.MOM.WW.ScadaBridge.Commons.Entities.Kpi; using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Repositories; using ZB.MOM.WW.ScadaBridge.Commons.Types.Kpi; namespace ZB.MOM.WW.ScadaBridge.ConfigurationDatabase.Repositories; /// /// EF Core implementation of over the central /// KpiSample table (M6 "KPI History & Trends"). See the interface for the /// contract; this class adds notes on the data-access strategy per method. /// public sealed class KpiHistoryRepository : IKpiHistoryRepository { private readonly ScadaBridgeDbContext _context; /// /// Initializes a new instance of the class. /// /// The EF Core database context. public KpiHistoryRepository(ScadaBridgeDbContext context) { _context = context ?? throw new ArgumentNullException(nameof(context)); } /// public async Task RecordSamplesAsync( IReadOnlyCollection samples, CancellationToken cancellationToken = default) { ArgumentNullException.ThrowIfNull(samples); // Avoid a no-op SaveChanges round-trip on quiet sampling ticks. if (samples.Count == 0) { return; } // Bulk-insert one sampling pass. AddRange + a single SaveChanges keeps the // whole batch in one round-trip; the store assigns each row's identity. _context.KpiSamples.AddRange(samples); await _context.SaveChangesAsync(cancellationToken); } /// public async Task> GetRawSeriesAsync( string source, string metric, string scope, string? scopeKey, DateTime fromUtc, DateTime toUtc, CancellationToken cancellationToken = default) { // The ScopeKey == scopeKey comparison is intentional: when scopeKey is null // EF translates it to "ScopeKey IS NULL", which matches the Global-scope rows // (null key) and excludes the site/node-scoped rows that carry a non-null key. return await _context.KpiSamples .Where(s => s.Source == source && s.Metric == metric && s.Scope == scope && s.ScopeKey == scopeKey && s.CapturedAtUtc >= fromUtc && s.CapturedAtUtc <= toUtc) .OrderBy(s => s.CapturedAtUtc) .Select(s => new KpiSeriesPoint(s.CapturedAtUtc, s.Value)) .ToListAsync(cancellationToken); } /// public async Task PurgeOlderThanAsync(DateTime before, CancellationToken cancellationToken = default) { // Set-based delete — no entity materialisation; returns the rows affected. return await _context.KpiSamples .Where(s => s.CapturedAtUtc < before) .ExecuteDeleteAsync(cancellationToken); } }