diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Dictionary/Tree/ChildrenDictionaryTreeController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Dictionary/Tree/ChildrenDictionaryTreeController.cs index 1d70c4cab9..bb3c08d8d9 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Dictionary/Tree/ChildrenDictionaryTreeController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Dictionary/Tree/ChildrenDictionaryTreeController.cs @@ -27,15 +27,8 @@ public class ChildrenDictionaryTreeController : DictionaryTreeControllerBase return BadRequest(error); } - IDictionaryItem[] dictionaryItems = PaginatedDictionaryItems( - pageNumber, - pageSize, - await DictionaryItemService.GetChildrenAsync(parentId), - out var totalItems); + PagedModel paginatedItems = await DictionaryItemService.GetPagedAsync(parentId, skip, take); - EntityTreeItemResponseModel[] viewModels = await MapTreeItemViewModels(parentId, dictionaryItems); - - PagedViewModel result = PagedViewModel(viewModels, totalItems); - return await Task.FromResult(Ok(result)); + return Ok(PagedViewModel(await MapTreeItemViewModels(parentId, paginatedItems.Items), paginatedItems.Total)); } } diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Dictionary/Tree/DictionaryTreeControllerBase.cs b/src/Umbraco.Cms.Api.Management/Controllers/Dictionary/Tree/DictionaryTreeControllerBase.cs index e87a2a8985..b51506c8da 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Dictionary/Tree/DictionaryTreeControllerBase.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Dictionary/Tree/DictionaryTreeControllerBase.cs @@ -27,11 +27,11 @@ public class DictionaryTreeControllerBase : EntityTreeControllerBase MapTreeItemViewModels(Guid? parentKey, IDictionaryItem[] dictionaryItems) + protected async Task> MapTreeItemViewModels(Guid? parentKey, IEnumerable dictionaryItems) { async Task CreateEntityTreeItemViewModelAsync(IDictionaryItem dictionaryItem) { - var hasChildren = (await DictionaryItemService.GetChildrenAsync(dictionaryItem.Key)).Any(); + var hasChildren = await DictionaryItemService.CountChildrenAsync(dictionaryItem.Key) > 0; return new EntityTreeItemResponseModel { Name = dictionaryItem.ItemKey, @@ -43,25 +43,6 @@ public class DictionaryTreeControllerBase : EntityTreeControllerBase(dictionaryItems.Length); - foreach (IDictionaryItem dictionaryItem in dictionaryItems) - { - items.Add(await CreateEntityTreeItemViewModelAsync(dictionaryItem)); - } - - return items.ToArray(); - } - - // language service does not (yet) allow pagination of dictionary items, we have to do it in memory for now - protected IDictionaryItem[] PaginatedDictionaryItems(long pageNumber, int pageSize, IEnumerable allDictionaryItems, out long totalItems) - { - IDictionaryItem[] allDictionaryItemsAsArray = allDictionaryItems.ToArray(); - - totalItems = allDictionaryItemsAsArray.Length; - return allDictionaryItemsAsArray - .OrderBy(item => item.ItemKey) - .Skip((int)pageNumber * pageSize) - .Take(pageSize) - .ToArray(); + return await Task.WhenAll(dictionaryItems.Select(CreateEntityTreeItemViewModelAsync)); } } diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Dictionary/Tree/RootDictionaryTreeController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Dictionary/Tree/RootDictionaryTreeController.cs index 24e66bf281..b8cc96dbb1 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Dictionary/Tree/RootDictionaryTreeController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Dictionary/Tree/RootDictionaryTreeController.cs @@ -27,15 +27,8 @@ public class RootDictionaryTreeController : DictionaryTreeControllerBase return BadRequest(error); } - IDictionaryItem[] dictionaryItems = PaginatedDictionaryItems( - pageNumber, - pageSize, - await DictionaryItemService.GetAtRootAsync(), - out var totalItems); + PagedModel paginatedItems = await DictionaryItemService.GetPagedAsync(null, skip, take); - EntityTreeItemResponseModel[] viewModels = await MapTreeItemViewModels(null, dictionaryItems); - - PagedViewModel result = PagedViewModel(viewModels, totalItems); - return await Task.FromResult(Ok(result)); + return Ok(PagedViewModel(await MapTreeItemViewModels(null, paginatedItems.Items), paginatedItems.Total)); } } diff --git a/src/Umbraco.Cms.Api.Management/Controllers/RelationType/Tree/RelationTypeTreeControllerBase.cs b/src/Umbraco.Cms.Api.Management/Controllers/RelationType/Tree/RelationTypeTreeControllerBase.cs index 6f7a0a4ce0..77c5f88285 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/RelationType/Tree/RelationTypeTreeControllerBase.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/RelationType/Tree/RelationTypeTreeControllerBase.cs @@ -25,7 +25,7 @@ public class RelationTypeTreeControllerBase : EntityTreeControllerBase UmbracoObjectTypes.RelationType; - protected EntityTreeItemResponseModel[] MapTreeItemViewModels(Guid? parentKey, IRelationType[] relationTypes) + protected IEnumerable MapTreeItemViewModels(Guid? parentKey, IEnumerable relationTypes) => relationTypes.Select(relationType => new EntityTreeItemResponseModel { Name = relationType.Name!, @@ -34,5 +34,5 @@ public class RelationTypeTreeControllerBase : EntityTreeControllerBase), StatusCodes.Status200OK)] public async Task>> Root(int skip = 0, int take = 100) { - if (PaginationService.ConvertSkipTakeToPaging(skip, take, out var pageNumber, out var pageSize, out ProblemDetails? error) == false) - { - return BadRequest(error); - } + PagedModel pagedRelationTypes = await _relationService.GetPagedRelationTypesAsync(skip, take); - // pagination is not supported (yet) by relation service, so we do it in memory for now - // - chances are we won't have many relation types, so it won't be an actual issue - IRelationType[] allRelationTypes = _relationService.GetAllRelationTypes().ToArray(); + PagedViewModel pagedResult = PagedViewModel( + MapTreeItemViewModels(null, pagedRelationTypes.Items), + pagedRelationTypes.Total); - EntityTreeItemResponseModel[] viewModels = MapTreeItemViewModels( - null, - allRelationTypes - .OrderBy(relationType => relationType.Name) - .Skip((int)(pageNumber * pageSize)) - .Take(pageSize) - .ToArray()); - - PagedViewModel result = PagedViewModel(viewModels, allRelationTypes.Length); - return await Task.FromResult(Ok(result)); + return Ok(pagedResult); } } diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Tree/FolderTreeControllerBase.cs b/src/Umbraco.Cms.Api.Management/Controllers/Tree/FolderTreeControllerBase.cs index 6e93d1eeac..6bd810dc1e 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Tree/FolderTreeControllerBase.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Tree/FolderTreeControllerBase.cs @@ -66,6 +66,15 @@ public abstract class FolderTreeControllerBase : EntityTreeControllerBase { totalItems = 0; + if (pageSize == 0) + { + totalItems = _foldersOnly + ? EntityService.CountChildren(parentId, FolderObjectType) + : EntityService.CountChildren(parentId, FolderObjectType) + + EntityService.CountChildren(parentId, ItemObjectType); + return Array.Empty(); + } + // EntityService is not able to paginate children of multiple item types, so we will only paginate the // item type entities and always return all folders as part of the the first result page IEntitySlim[] folderEntities = pageNumber == 0 @@ -82,6 +91,12 @@ public abstract class FolderTreeControllerBase : EntityTreeControllerBase ordering: ItemOrdering) .ToArray(); + // the GetChildren for folders does not return an amount and does not get executed when beyond the first page + // but the items still count towards the total, so add these to either 0 when only folders, or the out param from paged + totalItems += pageNumber == 0 + ? folderEntities.Length + : EntityService.CountChildren(parentId, FolderObjectType); + return folderEntities.Union(itemEntities).ToArray(); } } diff --git a/src/Umbraco.Cms.Api.Management/Services/Paging/PaginationService.cs b/src/Umbraco.Cms.Api.Management/Services/Paging/PaginationService.cs index ea8dde174c..3800b3b5dc 100644 --- a/src/Umbraco.Cms.Api.Management/Services/Paging/PaginationService.cs +++ b/src/Umbraco.Cms.Api.Management/Services/Paging/PaginationService.cs @@ -12,12 +12,12 @@ internal static class PaginationService { internal static bool ConvertSkipTakeToPaging(int skip, int take, out long pageNumber, out int pageSize, out ProblemDetails? error) { - if (take <= 0) + if (take < 0) { - throw new ArgumentException("Must be greater than zero", nameof(take)); + throw new ArgumentException("Must be equal to or greater than zero", nameof(take)); } - if (skip % take != 0) + if (take != 0 && skip % take != 0) { pageSize = 0; pageNumber = 0; @@ -32,7 +32,7 @@ internal static class PaginationService } pageSize = take; - pageNumber = skip / take; + pageNumber = take == 0 ? 0 : skip / take; error = null; return true; } diff --git a/src/Umbraco.Core/Models/PagedResult.cs b/src/Umbraco.Core/Models/PagedResult.cs index d60fb707ca..15e0576202 100644 --- a/src/Umbraco.Core/Models/PagedResult.cs +++ b/src/Umbraco.Core/Models/PagedResult.cs @@ -5,6 +5,7 @@ namespace Umbraco.Cms.Core.Models; /// /// Represents a paged result for a model collection /// +[Obsolete ("Superseded by PagedModel for service layer and below OR PagedViewModel in apis. Expected to be removed when skip/take pattern has been fully implemented v14+")] [DataContract(Name = "pagedCollection", Namespace = "")] public abstract class PagedResult { diff --git a/src/Umbraco.Core/Models/PagedResultOfT.cs b/src/Umbraco.Core/Models/PagedResultOfT.cs index c2d11a4f82..f84dd89f7d 100644 --- a/src/Umbraco.Core/Models/PagedResultOfT.cs +++ b/src/Umbraco.Core/Models/PagedResultOfT.cs @@ -7,6 +7,7 @@ namespace Umbraco.Cms.Core.Models; /// /// [DataContract(Name = "pagedCollection", Namespace = "")] +[Obsolete ("Superseded by PagedModel for service layer and below OR PagedViewModel in apis. Expected to be removed when skip/take pattern has been fully implemented v14+")] public class PagedResult : PagedResult { public PagedResult(long totalItems, long pageNumber, long pageSize) diff --git a/src/Umbraco.Core/Persistence/IQueryRepository.cs b/src/Umbraco.Core/Persistence/IQueryRepository.cs index e0e507abc1..0067b182ff 100644 --- a/src/Umbraco.Core/Persistence/IQueryRepository.cs +++ b/src/Umbraco.Core/Persistence/IQueryRepository.cs @@ -15,5 +15,5 @@ public interface IQueryRepository : IRepository /// /// Counts entities. /// - int Count(IQuery query); + int Count(IQuery? query); } diff --git a/src/Umbraco.Core/Persistence/Repositories/IEntityRepository.cs b/src/Umbraco.Core/Persistence/Repositories/IEntityRepository.cs index 025d291a72..2914a80aca 100644 --- a/src/Umbraco.Core/Persistence/Repositories/IEntityRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/IEntityRepository.cs @@ -83,4 +83,6 @@ public interface IEntityRepository : IRepository out long totalRecords, IQuery? filter, Ordering? ordering); + + int CountByQuery(IQuery query, Guid objectType, IQuery? filter); } diff --git a/src/Umbraco.Core/Services/DictionaryItemService.cs b/src/Umbraco.Core/Services/DictionaryItemService.cs index 00d6b35104..a64fd26f89 100644 --- a/src/Umbraco.Core/Services/DictionaryItemService.cs +++ b/src/Umbraco.Core/Services/DictionaryItemService.cs @@ -72,10 +72,39 @@ internal sealed class DictionaryItemService : RepositoryService, IDictionaryItem } } + /// + /// Gets the dictionary items in a paged manner. + /// Currently implements the paging in memory on the itenkey property because the underlying repository does not support paging yet + /// + public async Task> GetPagedAsync(Guid? parentId, int skip, int take) + { + using ICoreScope coreScope = ScopeProvider.CreateCoreScope(autoComplete: true); + + if (take == 0) + { + return parentId is null + ? new PagedModel(await CountRootAsync(), Enumerable.Empty()) + : new PagedModel(await CountChildrenAsync(parentId.Value), Enumerable.Empty()); + } + + IDictionaryItem[] items = (parentId is null + ? await GetAtRootAsync() + : await GetChildrenAsync(parentId.Value)).ToArray(); + + return new PagedModel( + items.Length, + items.OrderBy(i => i.ItemKey) + .Skip(skip) + .Take(take)); + } + /// public async Task> GetChildrenAsync(Guid parentId) => await GetByQueryAsync(Query().Where(x => x.ParentId == parentId)); + public async Task CountChildrenAsync(Guid parentId) + => await CountByQueryAsync(Query().Where(x => x.ParentId == parentId)); + /// public async Task> GetDescendantsAsync(Guid? parentId) { @@ -90,6 +119,9 @@ internal sealed class DictionaryItemService : RepositoryService, IDictionaryItem public async Task> GetAtRootAsync() => await GetByQueryAsync(Query().Where(x => x.ParentId == null)); + public async Task CountRootAsync() + => await CountByQueryAsync(Query().Where(x => x.ParentId == null)); + /// public async Task ExistsAsync(string key) { @@ -237,6 +269,15 @@ internal sealed class DictionaryItemService : RepositoryService, IDictionaryItem } } + private async Task CountByQueryAsync(IQuery query) + { + using (ScopeProvider.CreateCoreScope(autoComplete: true)) + { + var items = _dictionaryRepository.Count(query); + return await Task.FromResult(items); + } + } + private async Task> SaveAsync( IDictionaryItem dictionaryItem, Func operationValidation, diff --git a/src/Umbraco.Core/Services/EntityService.cs b/src/Umbraco.Core/Services/EntityService.cs index ed35e78879..e52a4712de 100644 --- a/src/Umbraco.Core/Services/EntityService.cs +++ b/src/Umbraco.Core/Services/EntityService.cs @@ -537,6 +537,19 @@ public class EntityService : RepositoryService, IEntityService } } + public int CountChildren( + int id, + UmbracoObjectTypes objectType, + IQuery? filter = null) + { + using (ScopeProvider.CreateCoreScope(autoComplete: true)) + { + IQuery query = Query().Where(x => x.ParentId == id && x.Trashed == false); + + return _entityRepository.CountByQuery(query, objectType.GetGuid(), filter); + } + } + // gets the object type, throws if not supported private UmbracoObjectTypes GetObjectType(Type? type) { @@ -562,6 +575,12 @@ public class EntityService : RepositoryService, IEntityService { IQuery query = Query().Where(x => x.ParentId == id && x.Trashed == trashed); + if (pageSize == 0) + { + totalRecords = _entityRepository.CountByQuery(query, objectType.GetGuid(), filter); + return Enumerable.Empty(); + } + return _entityRepository.GetPagedResultsByQuery(query, objectType.GetGuid(), pageIndex, pageSize, out totalRecords, filter, ordering); } } diff --git a/src/Umbraco.Core/Services/IDictionaryItemService.cs b/src/Umbraco.Core/Services/IDictionaryItemService.cs index 19f380c4ab..10855b0f25 100644 --- a/src/Umbraco.Core/Services/IDictionaryItemService.cs +++ b/src/Umbraco.Core/Services/IDictionaryItemService.cs @@ -1,4 +1,5 @@ using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Persistence.Querying; using Umbraco.Cms.Core.Services.OperationStatus; namespace Umbraco.Cms.Core.Services; @@ -98,4 +99,13 @@ public interface IDictionaryItemService /// Id of the new parent, null if the item should be moved to the root /// Key of the user moving the dictionary item Task> MoveAsync(IDictionaryItem dictionaryItem, Guid? parentId, Guid userKey); + + Task CountChildrenAsync(Guid parentId); + Task CountRootAsync(); + + /// + /// Gets the dictionary items in a paged manner. + /// Currently implements the paging in memory on the itenkey property because the underlying repository does not support paging yet + /// + Task> GetPagedAsync(Guid? parentId, int skip, int take); } diff --git a/src/Umbraco.Core/Services/IEntityService.cs b/src/Umbraco.Core/Services/IEntityService.cs index a2204b97ce..02959841dd 100644 --- a/src/Umbraco.Core/Services/IEntityService.cs +++ b/src/Umbraco.Core/Services/IEntityService.cs @@ -308,4 +308,9 @@ public interface IEntityService /// The identifier. /// When a new content or a media is saved with the key, it will have the reserved identifier. int ReserveId(Guid key); + + /// + /// Counts the children of an entity + /// + int CountChildren(int id, UmbracoObjectTypes objectType, IQuery? filter = null); } diff --git a/src/Umbraco.Core/Services/IRelationService.cs b/src/Umbraco.Core/Services/IRelationService.cs index b8b8000502..04e99528b5 100644 --- a/src/Umbraco.Core/Services/IRelationService.cs +++ b/src/Umbraco.Core/Services/IRelationService.cs @@ -171,7 +171,7 @@ public interface IRelationService : IService /// /// /// - IEnumerable GetPagedByRelationTypeId(int relationTypeId, long pageIndex, int pageSize, out long totalRecords, Ordering? ordering = null);/// + IEnumerable GetPagedByRelationTypeId(int relationTypeId, long pageIndex, int pageSize, out long totalRecords, Ordering? ordering = null); /// /// Gets a paged result of @@ -397,4 +397,13 @@ public interface IRelationService : IService IEnumerable GetAllowedObjectTypes(); Task> GetPagedByChildKeyAsync(Guid childKey, int skip, int take, string? relationTypeAlias); + int CountRelationTypes(); + + /// + /// Gets the Relation types in a paged manner. + /// Currently implements the paging in memory on the name attribute because the underlying repository does not support paging yet + /// + /// + /// + Task> GetPagedRelationTypesAsync(int skip, int take, params int[] ids); } diff --git a/src/Umbraco.Core/Services/RelationService.cs b/src/Umbraco.Core/Services/RelationService.cs index 9d5a392834..6c54ee6e54 100644 --- a/src/Umbraco.Core/Services/RelationService.cs +++ b/src/Umbraco.Core/Services/RelationService.cs @@ -122,6 +122,33 @@ public class RelationService : RepositoryService, IRelationService } } + /// + /// Gets the Relation types in a paged manner. + /// Currently implements the paging in memory on the name property because the underlying repository does not support paging yet + /// + public async Task> GetPagedRelationTypesAsync(int skip, int take, params int[] ids) + { + using ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true); + + if (take == 0) + { + return new PagedModel(CountRelationTypes(), Enumerable.Empty()); + } + + IRelationType[] items = await Task.FromResult(_relationTypeRepository.GetMany(ids).ToArray()); + + return new PagedModel( + items.Length, + items.OrderBy(relationType => relationType.Name) + .Skip(skip) + .Take(take)); + } + + public int CountRelationTypes() + { + return _relationTypeRepository.Count(null); + } + /// public IEnumerable GetByParentId(int id) => GetByParentId(id, null); diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/EntityRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/EntityRepository.cs index 0df84fa20f..e8bf7059e6 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/EntityRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/EntityRepository.cs @@ -30,6 +30,30 @@ internal class EntityRepository : RepositoryBase, IEntityRepositoryExtended #region Repository + public int CountByQuery(IQuery query, Guid objectType, IQuery? filter) + { + Sql sql = Sql(); + sql.SelectCount(); + sql + .From(); + sql.WhereIn(x => x.NodeObjectType, new[] { objectType } ); + + foreach (Tuple queryClause in query.GetWhereClauses()) + { + sql.Where(queryClause.Item1, queryClause.Item2); + } + + if (filter is not null) + { + foreach (Tuple filterClause in filter.GetWhereClauses()) + { + sql.Where(filterClause.Item1, filterClause.Item2); + } + } + + return Database.ExecuteScalar(sql); + } + public IEnumerable GetPagedResultsByQuery(IQuery query, Guid objectType, long pageIndex, int pageSize, out long totalRecords, IQuery? filter, Ordering? ordering) => diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/EntityRepositoryBase.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/EntityRepositoryBase.cs index 25d53e00ae..aeb8f1766e 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/EntityRepositoryBase.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/EntityRepositoryBase.cs @@ -181,7 +181,7 @@ public abstract class EntityRepositoryBase : RepositoryBase, IRead /// /// Returns an integer with the count of entities found with the passed in query /// - public int Count(IQuery query) + public int Count(IQuery? query) => PerformCount(query); /// @@ -221,9 +221,14 @@ public abstract class EntityRepositoryBase : RepositoryBase, IRead return count == 1; } - protected virtual int PerformCount(IQuery query) + protected virtual int PerformCount(IQuery? query) { Sql sqlClause = GetBaseQuery(true); + if (query is null) + { + return Database.ExecuteScalar(sqlClause); + } + var translator = new SqlTranslator(sqlClause, query); Sql sql = translator.Translate(); diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ExternalLoginRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ExternalLoginRepository.cs index 352b1dd3fd..2c0f0f8c2d 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ExternalLoginRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ExternalLoginRepository.cs @@ -46,7 +46,7 @@ internal class ExternalLoginRepository : EntityRepositoryBase /// /// - public int Count(IQuery query) + public int Count(IQuery? query) { Sql sql = Sql().SelectCount().From(); return Database.ExecuteScalar(sql); diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/RedirectUrlRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/RedirectUrlRepository.cs index 7e4c56f117..f598df7168 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/RedirectUrlRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/RedirectUrlRepository.cs @@ -158,7 +158,7 @@ internal class RedirectUrlRepository : EntityRepositoryBase, return rules; } - protected override int PerformCount(IQuery query) => + protected override int PerformCount(IQuery? query) => throw new NotSupportedException("This repository does not support this method."); protected override bool PerformExists(Guid id) => PerformGet(id) != null; diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ServerRegistrationRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ServerRegistrationRepository.cs index b6d5221b59..b758150de1 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ServerRegistrationRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ServerRegistrationRepository.cs @@ -45,7 +45,7 @@ internal class ServerRegistrationRepository : EntityRepositoryBase(AppCaches.RuntimeCache, ScopeAccessor, GetEntityId, /*expires:*/ false); - protected override int PerformCount(IQuery query) => + protected override int PerformCount(IQuery? query) => throw new NotSupportedException("This repository does not support this method."); protected override bool PerformExists(int id) =>