diff --git a/DeepDrftData/Repositories/TrackRepository.cs b/DeepDrftData/Repositories/TrackRepository.cs index 9fc5636..5b51cac 100644 --- a/DeepDrftData/Repositories/TrackRepository.cs +++ b/DeepDrftData/Repositories/TrackRepository.cs @@ -11,42 +11,39 @@ namespace DeepDrftData.Repositories; public class TrackRepository : Repository { - private readonly DeepDrftContext _context; - public TrackRepository( DeepDrftContext context, ILogger> logger, IDbExceptionClassifier? classifier = null) : base(context, logger, classifier: classifier) { - _context = context; } // Lookup by vault entry key. The base Repository<> only exposes id-based queries, so this - // queries the DbSet directly. Returns null on miss (service wraps in ResultContainer). + // uses Query (soft-delete filtered) rather than the raw DbSet. public async Task GetByEntryKeyAsync(string entryKey) - => await _context.Tracks.FirstOrDefaultAsync(t => t.EntryKey == entryKey); + => await Query.FirstOrDefaultAsync(t => t.EntryKey == entryKey); // Picks one track uniformly at random. Two round-trips (count, then a single offset row) // rather than ORDER BY random() so the database never sorts the whole table — the catalogue // is small today but this keeps the cost flat as it grows. Returns null when empty so the - // service surfaces a valid empty-library state, not an error. Queries the DbSet directly, - // mirroring GetByEntryKeyAsync, since the base Repository<> exposes only id-based reads. + // service surfaces a valid empty-library state, not an error. Uses Query (soft-delete + // filtered) so deleted tracks are never candidates. public async Task GetRandomAsync(CancellationToken cancellationToken = default) { - var count = await _context.Tracks.CountAsync(cancellationToken); + var count = await Query.CountAsync(cancellationToken); if (count == 0) return null; var index = Random.Shared.Next(count); - return await _context.Tracks + return await Query .OrderBy(t => t.Id) .Skip(index) .Take(1) .FirstOrDefaultAsync(cancellationToken); } - // Paged query with optional filter predicates. Built directly off the DbSet rather than the + // Paged query with optional filter predicates. Built off Query (soft-delete filtered) rather than the // base GetPagedAsync(paging) overload, which takes no where-clause. The OrderBy expression and // direction ride in on the PagingParameters the manager already built, so sort + filter + // pagination compose. Filter predicates apply before sort and Skip/Take so TotalCount reflects @@ -56,7 +53,7 @@ public class TrackRepository : Repository TrackFilter? filter, CancellationToken ct = default) { - IQueryable query = _context.Tracks; + IQueryable query = Query; if (filter is not null) { @@ -105,7 +102,7 @@ public class TrackRepository : Repository // Distinct albums (non-null) with track counts and a representative cover key. The cover is the // first non-null ImagePath in the group; GroupBy + projection keeps it a single round-trip. public async Task> GetDistinctAlbumsAsync(CancellationToken ct = default) - => await _context.Tracks + => await Query .Where(t => t.Album != null) .GroupBy(t => t.Album!) .Select(g => new AlbumSummaryDto @@ -123,7 +120,7 @@ public class TrackRepository : Repository // Distinct genres (non-null) with track counts. public async Task> GetDistinctGenresAsync(CancellationToken ct = default) - => await _context.Tracks + => await Query .Where(t => t.Genre != null) .GroupBy(t => t.Genre!) .Select(g => new GenreSummaryDto