feat(kpi): K2 — KpiSample EF mapping + KpiHistoryRepository + AddKpiSampleTable migration
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
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 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);
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user