diff --git a/src/BUTR.Site.NexusMods.Server.ValueObjects.Vogen/BUTR.Site.NexusMods.Server.ValueObjects.Vogen.csproj b/src/BUTR.Site.NexusMods.Server.ValueObjects.Vogen/BUTR.Site.NexusMods.Server.ValueObjects.Vogen.csproj index 474e6ffc..dacdc498 100644 --- a/src/BUTR.Site.NexusMods.Server.ValueObjects.Vogen/BUTR.Site.NexusMods.Server.ValueObjects.Vogen.csproj +++ b/src/BUTR.Site.NexusMods.Server.ValueObjects.Vogen/BUTR.Site.NexusMods.Server.ValueObjects.Vogen.csproj @@ -12,7 +12,7 @@ - + diff --git a/src/BUTR.Site.NexusMods.Server/BUTR.Site.NexusMods.Server.csproj b/src/BUTR.Site.NexusMods.Server/BUTR.Site.NexusMods.Server.csproj index a176a5d8..8c80d1aa 100644 --- a/src/BUTR.Site.NexusMods.Server/BUTR.Site.NexusMods.Server.csproj +++ b/src/BUTR.Site.NexusMods.Server/BUTR.Site.NexusMods.Server.csproj @@ -17,35 +17,35 @@ - + - + - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + - - + + - + - + @@ -53,10 +53,10 @@ - + - - + + diff --git a/src/BUTR.Site.NexusMods.Server/Controllers/ExposedModsController.cs b/src/BUTR.Site.NexusMods.Server/Controllers/ExposedModsController.cs index 715f0d0b..c2d7fca9 100644 --- a/src/BUTR.Site.NexusMods.Server/Controllers/ExposedModsController.cs +++ b/src/BUTR.Site.NexusMods.Server/Controllers/ExposedModsController.cs @@ -30,7 +30,7 @@ public ExposedModsController(ILogger logger, IUnitOfWorkF [HttpPost("Paginated")] public async Task?>> GetPaginatedAsync([FromBody, Required] PaginatedQuery query, CancellationToken ct) { - await using var unitOfRead = _unitOfWorkFactory.CreateUnitOfRead(TenantId.None); + await using var unitOfRead = _unitOfWorkFactory.CreateUnitOfRead(); var paginated = await unitOfRead.NexusModsModModules.GetExposedPaginatedAsync(query, ct); @@ -40,7 +40,7 @@ public ExposedModsController(ILogger logger, IUnitOfWorkF [HttpGet("Autocomplete/ModuleIds")] public async Task?>> GetAutocompleteModuleIdsAsync([FromQuery, Required] ModuleId moduleId) { - await using var unitOfRead = _unitOfWorkFactory.CreateUnitOfRead(TenantId.None); + await using var unitOfRead = _unitOfWorkFactory.CreateUnitOfRead(); var moduleIds = await unitOfRead.Autocompletes.AutocompleteStartsWithAsync(x => x.Module.ModuleId, moduleId, CancellationToken.None); diff --git a/src/BUTR.Site.NexusMods.Server/Extensions/QueryableExtensions.cs b/src/BUTR.Site.NexusMods.Server/Extensions/QueryableExtensions.cs index b5c15efd..1e291a83 100644 --- a/src/BUTR.Site.NexusMods.Server/Extensions/QueryableExtensions.cs +++ b/src/BUTR.Site.NexusMods.Server/Extensions/QueryableExtensions.cs @@ -74,6 +74,7 @@ public static async Task> PaginatedAsync(this IQueryabl where TEntity : class { var startTime = Stopwatch.GetTimestamp(); + var count = await queryable.CountAsync(ct); return new() @@ -89,6 +90,49 @@ public static async Task> PaginatedAsync(this IQueryabl } }; } + + public static Task> PaginatedGroupedAsync(this IQueryable queryable, PaginatedQuery query, uint maxPageSize = 20, Sorting? defaultSorting = default, CancellationToken ct = default) + where TEntity : class + { + var page = query.Page; + var pageSize = Math.Max(Math.Min(query.PageSize, maxPageSize), 5); + var filters = query.Filters ?? Enumerable.Empty(); + var sortings = query.Sortings is null || query.Sortings.Count == 0 + ? defaultSorting == null ? Array.Empty() : new List { defaultSorting } + : query.Sortings; + + return queryable + .WithFilter(filters) + .WithSort(sortings) + .PaginatedGroupedAsync(page, pageSize, ct); + } + + public static async Task> PaginatedGroupedAsync(this IQueryable queryable, uint page, uint pageSize, CancellationToken ct = default) + where TEntity : class + { + var startTime = Stopwatch.GetTimestamp(); + + var response = await queryable.GroupBy(_ => 1) + .Select(x => new + { + PageItems = x.Skip((int) ((page - 1) * pageSize)).Take((int) pageSize).ToList(), + Total = x.Count() + }) + .FirstAsync(cancellationToken: ct); + + return new() + { + StartTime = startTime, + Items = response.PageItems.ToAsyncEnumerable(), + Metadata = new() + { + PageSize = pageSize, + CurrentPage = page, + TotalCount = (uint) response.Total, + TotalPages = (uint) Math.Floor((double) response.Total / (double) pageSize), + } + }; + } public static Task> ToImmutableArrayAsync(this IQueryable source, CancellationToken ct = default) { diff --git a/src/BUTR.Site.NexusMods.Server/Repositories/CrashReportEntityRepository.cs b/src/BUTR.Site.NexusMods.Server/Repositories/CrashReportEntityRepository.cs index 3f221068..d795dcb0 100644 --- a/src/BUTR.Site.NexusMods.Server/Repositories/CrashReportEntityRepository.cs +++ b/src/BUTR.Site.NexusMods.Server/Repositories/CrashReportEntityRepository.cs @@ -104,6 +104,6 @@ IQueryable DbQueryBase(Expression moduleIds.Contains(y.Module.ModuleId)) || x.ModuleInfos.Where(y => y.NexusModsMod != null).Any(y => nexusModsModIds.Contains(y.NexusModsMod!.NexusModsModId))); - return await dbQuery.PaginatedAsync(query.Page, query.PageSize, ct); + return await dbQuery.PaginatedGroupedAsync(query.Page, query.PageSize, ct); } } \ No newline at end of file diff --git a/src/BUTR.Site.NexusMods.Server/Repositories/IRepository.cs b/src/BUTR.Site.NexusMods.Server/Repositories/IRepository.cs index 29c56a78..46c47b23 100644 --- a/src/BUTR.Site.NexusMods.Server/Repositories/IRepository.cs +++ b/src/BUTR.Site.NexusMods.Server/Repositories/IRepository.cs @@ -129,12 +129,12 @@ public async Task> GetAllAsync(Expression> PaginatedAsync(PaginatedQuery query, uint maxPageSize = 20, Sorting? defaultSorting = default, CancellationToken ct = default) => InternalQuery - .PaginatedAsync(query, maxPageSize, defaultSorting, ct); + .PaginatedGroupedAsync(query, maxPageSize, defaultSorting, ct); public Task> PaginatedAsync(Expression> projection, PaginatedQuery query, uint maxPageSize = 20, Sorting? defaultSorting = default, CancellationToken ct = default) where TProjection : class => InternalQuery .Select(projection) - .PaginatedAsync(query, maxPageSize, defaultSorting, ct); + .PaginatedGroupedAsync(query, maxPageSize, defaultSorting, ct); public virtual void Add(TEntity entity) => _dbContext.Set() diff --git a/src/BUTR.Site.NexusMods.Server/Repositories/NexusModsModToModuleEntityRepository.cs b/src/BUTR.Site.NexusMods.Server/Repositories/NexusModsModToModuleEntityRepository.cs index 06022988..3664eeb5 100644 --- a/src/BUTR.Site.NexusMods.Server/Repositories/NexusModsModToModuleEntityRepository.cs +++ b/src/BUTR.Site.NexusMods.Server/Repositories/NexusModsModToModuleEntityRepository.cs @@ -57,10 +57,10 @@ public NexusModsModToModuleEntityRepository(IAppDbContextProvider appDbContextPr public async Task> GetByStaffPaginatedAsync(PaginatedQuery query, CancellationToken ct) => await _dbContext.NexusModsModModules .Where(x => x.LinkType == NexusModsModToModuleLinkType.ByStaff) - .GroupBy(x => x.Module.ModuleId) + .GroupBy(x => new { x.Module.ModuleId }) .Select(x => new LinkedByStaffModuleNexusModsModsModel { - ModuleId = x.Key, + ModuleId = x.Key.ModuleId, NexusModsMods = x.Select(y => new LinkedByStaffNexusModsModModel { NexusModsModId = y.NexusModsMod.NexusModsModId, @@ -71,10 +71,10 @@ public async Task> GetByStaffPagin public async Task> GetExposedPaginatedAsync(PaginatedQuery query, CancellationToken ct) => await _dbContext.NexusModsModModules .Where(x => x.LinkType == NexusModsModToModuleLinkType.ByUnverifiedFileExposure) - .GroupBy(x => x.NexusModsMod.NexusModsModId) + .GroupBy(x => new { x.NexusModsMod.NexusModsModId }) .Select(x => new LinkedByExposureNexusModsModModelsModel { - NexusModsModId = x.Key, + NexusModsModId = x.Key.NexusModsModId, Modules = x.Select(y => new LinkedByExposureModuleModel { ModuleId = y.Module.ModuleId, diff --git a/src/BUTR.Site.NexusMods.Server/Repositories/NexusModsUserRepository.cs b/src/BUTR.Site.NexusMods.Server/Repositories/NexusModsUserRepository.cs index ada0cd56..0a3e3201 100644 --- a/src/BUTR.Site.NexusMods.Server/Repositories/NexusModsUserRepository.cs +++ b/src/BUTR.Site.NexusMods.Server/Repositories/NexusModsUserRepository.cs @@ -124,7 +124,7 @@ public async Task> GetNexusModsModsPaginatedAsync(Nex }); return await availableModsByNexusModsModLinkage - .PaginatedAsync(query, 20, new() { Property = nameof(UserLinkedModModel.NexusModsModId), Type = SortingType.Ascending }, ct); + .PaginatedGroupedAsync(query, 20, new() { Property = nameof(UserLinkedModModel.NexusModsModId), Type = SortingType.Ascending }, ct); } public async Task> GetAvailableModsPaginatedAsync(NexusModsUserId userId, PaginatedQuery query, CancellationToken ct)