fix(backend): compute track listens count via SQL instead of an unloaded navigation
TrackApplicationResponse.FromEntity counted track.ListeningHistories, but none of the queries that built a response ever Included it, so the collection was always empty and listensCount was always 0. Each query now projects the count as a correlated subquery before mapping to the response.
This commit is contained in:
@@ -4,6 +4,7 @@ using Microsoft.EntityFrameworkCore;
|
||||
using Musify.Application.Contracts;
|
||||
using Musify.Application.Shared;
|
||||
using Musify.Application.Tracks.Responses;
|
||||
using X.PagedList;
|
||||
using X.PagedList.EF;
|
||||
|
||||
namespace Musify.Application.PlayLists
|
||||
@@ -32,11 +33,15 @@ namespace Musify.Application.PlayLists
|
||||
|
||||
var totalCount = await tracksQuery.CountAsync(cancellationToken);
|
||||
|
||||
var pagedTracks = await tracksQuery
|
||||
var pagedEntities = await tracksQuery
|
||||
.OrderBy(plt => plt.Position)
|
||||
.Select(plt => TrackApplicationResponse.FromEntity(plt.Track))
|
||||
.Select(plt => new { plt.Track, ListensCount = plt.Track.ListeningHistories.Count })
|
||||
.ToPagedListAsync(request.PageNumber, request.PageSize, totalCount, cancellationToken);
|
||||
|
||||
var pagedTracks = new StaticPagedList<TrackApplicationResponse>(
|
||||
pagedEntities.Select(x => TrackApplicationResponse.FromEntity(x.Track, x.ListensCount)),
|
||||
pagedEntities.PageNumber, pagedEntities.PageSize, pagedEntities.TotalItemCount);
|
||||
|
||||
return PaginatedResponse<TrackApplicationResponse>.FromPagedList(pagedTracks);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,7 +123,7 @@ namespace Musify.Application.Tracks
|
||||
return Error.Failure(description: $"Failed to create track for {request.Title}");
|
||||
}
|
||||
|
||||
return TrackApplicationResponse.FromEntity(trackEntity);
|
||||
return TrackApplicationResponse.FromEntity(trackEntity, listensCount: 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,10 +17,11 @@ namespace Musify.Application.Tracks
|
||||
var listeningHistory = database.ListeningHistories
|
||||
.Where(l => l.UserId == query.UserId)
|
||||
.OrderByDescending(l => l.ListenedAt)
|
||||
.Select(t => TrackApplicationResponse.FromEntity(t.Track))
|
||||
.Select(t => new { t.Track, ListensCount = t.Track.ListeningHistories.Count })
|
||||
.AsEnumerable()
|
||||
.DistinctBy(t => t.Track.Id)
|
||||
.Take(ListSize)
|
||||
.DistinctBy(t => t.Id)
|
||||
.AsEnumerable();
|
||||
.Select(t => TrackApplicationResponse.FromEntity(t.Track, t.ListensCount));
|
||||
|
||||
return ValueTask.FromResult(listeningHistory);
|
||||
}
|
||||
|
||||
@@ -14,15 +14,15 @@ namespace Musify.Application.Tracks
|
||||
{
|
||||
public async ValueTask<ErrorOr<TrackApplicationResponse>> Handle(GetTrackByIdQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
var track = await database.Tracks.AsNoTracking()
|
||||
var entity = await database.Tracks.AsNoTracking()
|
||||
.Where(t => t.Id == request.TrackId)
|
||||
.Select(t => TrackApplicationResponse.FromEntity(t))
|
||||
.Select(t => new { Track = t, ListensCount = t.ListeningHistories.Count })
|
||||
.SingleOrDefaultAsync(cancellationToken);
|
||||
|
||||
if (track is null)
|
||||
if (entity is null)
|
||||
return Error.NotFound();
|
||||
|
||||
return track;
|
||||
return TrackApplicationResponse.FromEntity(entity.Track, entity.ListensCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ using Mediator;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Musify.Application.Shared;
|
||||
using Musify.Application.Tracks.Responses;
|
||||
using X.PagedList;
|
||||
using X.PagedList.EF;
|
||||
using Musify.Application.Contracts;
|
||||
|
||||
@@ -29,11 +30,15 @@ namespace Musify.Application.Tracks
|
||||
|
||||
var totalCount = await tracksQuery.CountAsync(cancellationToken);
|
||||
|
||||
var pagedTracks = await tracksQuery
|
||||
var pagedEntities = await tracksQuery
|
||||
.OrderBy(t => t.Id)
|
||||
.Select(t => TrackApplicationResponse.FromEntity(t.Track))
|
||||
.Select(t => new { t.Track, ListensCount = t.Track.ListeningHistories.Count })
|
||||
.ToPagedListAsync(request.PageNumber, request.PageSize, totalCount, cancellationToken);
|
||||
|
||||
var pagedTracks = new StaticPagedList<TrackApplicationResponse>(
|
||||
pagedEntities.Select(x => TrackApplicationResponse.FromEntity(x.Track, x.ListensCount)),
|
||||
pagedEntities.PageNumber, pagedEntities.PageSize, pagedEntities.TotalItemCount);
|
||||
|
||||
return PaginatedResponse<TrackApplicationResponse>.FromPagedList(pagedTracks);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ using Mediator;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Musify.Application.Shared;
|
||||
using Musify.Application.Tracks.Responses;
|
||||
using X.PagedList;
|
||||
using X.PagedList.EF;
|
||||
using Musify.Application.Contracts;
|
||||
|
||||
@@ -36,11 +37,15 @@ namespace Musify.Application.Tracks
|
||||
|
||||
var totalCount = await tracksQuery.CountAsync(cancellationToken);
|
||||
|
||||
var pagedTracks = await tracksQuery
|
||||
var pagedEntities = await tracksQuery
|
||||
.OrderBy(t => t.Track.CreatedAt)
|
||||
.Select(t => TrackApplicationResponse.FromEntity(t.Track))
|
||||
.Select(t => new { t.Track, ListensCount = t.Track.ListeningHistories.Count })
|
||||
.ToPagedListAsync(pageNumber, pageSize, totalCount, cancellationToken);
|
||||
|
||||
var pagedTracks = new StaticPagedList<TrackApplicationResponse>(
|
||||
pagedEntities.Select(x => TrackApplicationResponse.FromEntity(x.Track, x.ListensCount)),
|
||||
pagedEntities.PageNumber, pagedEntities.PageSize, pagedEntities.TotalItemCount);
|
||||
|
||||
return PaginatedResponse<TrackApplicationResponse>.FromPagedList(pagedTracks);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,17 +10,13 @@ namespace Musify.Application.Tracks.Responses
|
||||
DateTime CreatedAt,
|
||||
DateTime UpdatedAt)
|
||||
{
|
||||
public static TrackApplicationResponse FromEntity(Track track)
|
||||
{
|
||||
var count = track.ListeningHistories.Count(x => x.TrackId == track.Id);
|
||||
|
||||
return new TrackApplicationResponse(
|
||||
public static TrackApplicationResponse FromEntity(Track track, int listensCount) =>
|
||||
new(
|
||||
track.Id,
|
||||
track.Title,
|
||||
track.Duration,
|
||||
count,
|
||||
listensCount,
|
||||
track.CreatedAt,
|
||||
track.UpdatedAt);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user