73 lines
2.9 KiB
C#
73 lines
2.9 KiB
C#
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;
|
|
|
|
/// <summary>
|
|
/// EF Core implementation of <see cref="IKpiHistoryRepository"/> over the central
|
|
/// <c>KpiSample</c> table (M6 "KPI History & Trends"). See the interface for the
|
|
/// contract; this class adds notes on the data-access strategy per method.
|
|
/// </summary>
|
|
public sealed class KpiHistoryRepository : IKpiHistoryRepository
|
|
{
|
|
private readonly ScadaBridgeDbContext _context;
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="KpiHistoryRepository"/> class.
|
|
/// </summary>
|
|
/// <param name="context">The EF Core database context.</param>
|
|
public KpiHistoryRepository(ScadaBridgeDbContext context)
|
|
{
|
|
_context = context ?? throw new ArgumentNullException(nameof(context));
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public async Task RecordSamplesAsync(
|
|
IReadOnlyCollection<KpiSample> 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);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public async Task<IReadOnlyList<KpiSeriesPoint>> 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);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public async Task<int> 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);
|
|
}
|
|
}
|