fix(persistence): drop broken Guid lookup on ISnapshotRepository (CRITICAL)
Snapshots are append-only and identified by the composite (EventId, CapturedAt), not a surrogate Guid. The previous implementation inherited IRepository<Guid, OddsSnapshot> and faked the lookup with (long)key.GetHashCode() on a long auto-increment PK — collision-prone and non-portable. Nobody called GetAsync(Guid) / DeleteAsync(Guid) anyway. * ISnapshotRepository no longer extends IRepository<Guid, OddsSnapshot>; it exposes only the methods snapshots actually have: ListAsync, ListByEventAsync, AddAsync, SaveChangesAsync. * SnapshotRepository drops the broken Get/Update/Delete methods.
This commit is contained in:
@@ -6,11 +6,23 @@ namespace Marathon.Application.Abstractions;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Repository for <see cref="OddsSnapshot"/> domain entities.
|
/// Repository for <see cref="OddsSnapshot"/> domain entities.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface ISnapshotRepository : IRepository<Guid, OddsSnapshot>
|
/// <remarks>
|
||||||
|
/// Snapshots are append-only and identified by the composite (EventId, CapturedAt)
|
||||||
|
/// rather than a surrogate key, so this contract intentionally does NOT extend
|
||||||
|
/// <see cref="IRepository{TKey, TEntity}"/> — point lookup by Guid would be
|
||||||
|
/// meaningless. Use <see cref="ListByEventAsync"/> for retrieval.
|
||||||
|
/// </remarks>
|
||||||
|
public interface ISnapshotRepository
|
||||||
{
|
{
|
||||||
|
Task<IReadOnlyList<OddsSnapshot>> ListAsync(CancellationToken ct = default);
|
||||||
|
|
||||||
Task<IReadOnlyList<OddsSnapshot>> ListByEventAsync(
|
Task<IReadOnlyList<OddsSnapshot>> ListByEventAsync(
|
||||||
EventId eventId,
|
EventId eventId,
|
||||||
DateTimeOffset from,
|
DateTimeOffset from,
|
||||||
DateTimeOffset to,
|
DateTimeOffset to,
|
||||||
CancellationToken ct = default);
|
CancellationToken ct = default);
|
||||||
|
|
||||||
|
Task AddAsync(OddsSnapshot entity, CancellationToken ct = default);
|
||||||
|
|
||||||
|
Task SaveChangesAsync(CancellationToken ct = default);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,18 +11,6 @@ internal sealed class SnapshotRepository : ISnapshotRepository
|
|||||||
|
|
||||||
public SnapshotRepository(MarathonDbContext db) => _db = db;
|
public SnapshotRepository(MarathonDbContext db) => _db = db;
|
||||||
|
|
||||||
public async Task<OddsSnapshot?> GetAsync(Guid key, CancellationToken ct = default)
|
|
||||||
{
|
|
||||||
var entity = await _db.Snapshots
|
|
||||||
.Include(s => s.Bets)
|
|
||||||
.FirstOrDefaultAsync(s => s.Id == (long)key.GetHashCode(), ct);
|
|
||||||
// Note: Guid→long mapping is lossy for GetAsync by Guid; the repo interface requires Guid key.
|
|
||||||
// Snapshots are typically retrieved by event, not directly by id.
|
|
||||||
// A proper implementation would store the Guid as a TEXT column.
|
|
||||||
// For now, this method is functionally available — callers prefer ListByEventAsync.
|
|
||||||
return entity is null ? null : Mapping.ToDomain(entity);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<IReadOnlyList<OddsSnapshot>> ListAsync(CancellationToken ct = default)
|
public async Task<IReadOnlyList<OddsSnapshot>> ListAsync(CancellationToken ct = default)
|
||||||
{
|
{
|
||||||
var entities = await _db.Snapshots.AsNoTracking()
|
var entities = await _db.Snapshots.AsNoTracking()
|
||||||
@@ -56,21 +44,6 @@ internal sealed class SnapshotRepository : ISnapshotRepository
|
|||||||
await _db.Snapshots.AddAsync(efEntity, ct);
|
await _db.Snapshots.AddAsync(efEntity, ct);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task UpdateAsync(OddsSnapshot entity, CancellationToken ct = default)
|
|
||||||
{
|
|
||||||
// Snapshots are immutable once written — update is not a typical operation.
|
|
||||||
var efEntity = Mapping.ToEntity(entity);
|
|
||||||
_db.Snapshots.Update(efEntity);
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task DeleteAsync(Guid key, CancellationToken ct = default)
|
|
||||||
{
|
|
||||||
var entity = await _db.Snapshots.FindAsync([(long)key.GetHashCode()], ct);
|
|
||||||
if (entity is not null)
|
|
||||||
_db.Snapshots.Remove(entity);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task SaveChangesAsync(CancellationToken ct = default) =>
|
public async Task SaveChangesAsync(CancellationToken ct = default) =>
|
||||||
await _db.SaveChangesAsync(ct);
|
await _db.SaveChangesAsync(ct);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user