From 6d5a37463fa6ff9724726d2e081c2b8274717b68 Mon Sep 17 00:00:00 2001 From: Alex Mamzikov Date: Tue, 7 May 2024 13:40:49 +0500 Subject: [PATCH 1/9] typed arguments --- .../Controllers/BasicBulkCrudController.cs | 2 +- .../Controllers/BasicCrudController.cs | 2 +- .../Controllers/BasicReadController.cs | 12 +++--------- .../Data/EmptyAdditionalFilters.cs | 12 ------------ .../Data/IReadFilterable.cs | 9 --------- .../Managers/EfModelManager.cs | 19 ++++++++++--------- .../Managers/EfSoftRemovableModelManager.cs | 19 ++++++++++--------- .../Managers/IModelManager.cs | 4 ++-- .../Services/IResourceBasedCrudService.cs | 3 ++- .../Services/IResourceBasedReadOnlyService.cs | 5 +++-- .../ResourceBasedDataManageableCrudService.cs | 5 +++-- ...ourceBasedDataManageableReadOnlyService.cs | 7 ++++--- 12 files changed, 39 insertions(+), 60 deletions(-) delete mode 100644 Wissance.WebApiToolkit/Wissance.WebApiToolkit/Data/EmptyAdditionalFilters.cs delete mode 100644 Wissance.WebApiToolkit/Wissance.WebApiToolkit/Data/IReadFilterable.cs diff --git a/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Controllers/BasicBulkCrudController.cs b/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Controllers/BasicBulkCrudController.cs index e9524f9..544f966 100644 --- a/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Controllers/BasicBulkCrudController.cs +++ b/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Controllers/BasicBulkCrudController.cs @@ -7,7 +7,7 @@ namespace Wissance.WebApiToolkit.Controllers { public class BasicBulkCrudController : BasicReadController where TRes : class - where TFilter: class, IReadFilterable + where TFilter: class { [HttpPost] [Route("api/bulk/[controller]")] diff --git a/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Controllers/BasicCrudController.cs b/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Controllers/BasicCrudController.cs index 7af5527..48b6c0b 100644 --- a/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Controllers/BasicCrudController.cs +++ b/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Controllers/BasicCrudController.cs @@ -9,7 +9,7 @@ namespace Wissance.WebApiToolkit.Controllers { public abstract class BasicCrudController : BasicReadController where TRes : class - where TFilter: class, IReadFilterable + where TFilter: class { [HttpPost] [Route("api/[controller]")] diff --git a/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Controllers/BasicReadController.cs b/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Controllers/BasicReadController.cs index bbf1e6e..0e1e4ae 100644 --- a/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Controllers/BasicReadController.cs +++ b/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Controllers/BasicReadController.cs @@ -1,10 +1,7 @@ using System; -using System.Collections; using System.Collections.Generic; -using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Primitives; using Wissance.WebApiToolkit.Data; using Wissance.WebApiToolkit.Dto; using Wissance.WebApiToolkit.Managers; @@ -15,7 +12,7 @@ namespace Wissance.WebApiToolkit.Controllers public abstract class BasicReadController : BasicPagedDataController where TRes: class - where TFilter: class, IReadFilterable + where TFilter: class { [HttpGet] [Route("api/[controller]")] @@ -25,10 +22,7 @@ public virtual async Task> ReadAsync([FromQuery] int? page, [ int pageNumber = GetPage(page); int pageSize = GetPageSize(size); SortOption sorting = !string.IsNullOrEmpty(sort) ? new SortOption(sort, order) : null; - IDictionary additionalQueryParams = additionalFilters != null - ? additionalFilters.SelectFilters() - : new Dictionary(); - OperationResultDto, long>> result = await Manager.GetAsync(pageNumber, pageSize, sorting, additionalQueryParams); + OperationResultDto, long>> result = await Manager.GetAsync(pageNumber, pageSize, sorting, additionalFilters); HttpContext.Response.StatusCode = result.Status; return new PagedDataDto(pageNumber, result.Data.Item2, PagingUtils.GetTotalPages(result.Data.Item2, pageSize), result.Data.Item1); } @@ -41,6 +35,6 @@ public async Task ReadByIdAsync([FromRoute] TId id) HttpContext.Response.StatusCode = result.Status; return result.Data; } - public IModelManager Manager { get; set; } + public IModelManager Manager { get; set; } } } diff --git a/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Data/EmptyAdditionalFilters.cs b/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Data/EmptyAdditionalFilters.cs deleted file mode 100644 index fffdf39..0000000 --- a/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Data/EmptyAdditionalFilters.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Collections.Generic; - -namespace Wissance.WebApiToolkit.Data -{ - public class EmptyAdditionalFilters : IReadFilterable - { - public IDictionary SelectFilters() - { - return new Dictionary(); - } - } -} \ No newline at end of file diff --git a/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Data/IReadFilterable.cs b/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Data/IReadFilterable.cs deleted file mode 100644 index 1fcdbfc..0000000 --- a/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Data/IReadFilterable.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.Collections.Generic; - -namespace Wissance.WebApiToolkit.Data -{ - public interface IReadFilterable - { - IDictionary SelectFilters(); - } -} \ No newline at end of file diff --git a/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Managers/EfModelManager.cs b/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Managers/EfModelManager.cs index 72a5383..183208f 100644 --- a/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Managers/EfModelManager.cs +++ b/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Managers/EfModelManager.cs @@ -23,10 +23,11 @@ namespace Wissance.WebApiToolkit.Managers /// Model class implements IModelIdentifiable /// DTO class (representation of Model in other systems i.e. in frontend)) /// Identifier type that is using as database table PK - public abstract class EfModelManager : IModelManager + public abstract class EfModelManager : IModelManager where TObj: class, IModelIdentifiable where TRes: class where TId: IComparable + where TFilter: class { /// @@ -34,14 +35,14 @@ public abstract class EfModelManager : IModelManager /// Ef Database context /// Delegate (factory func) for creating DTO from Model - /// Function that use dictionary with query params to filter result set + /// Function that use TFilter with query params to filter result set /// Logger factory /// - public EfModelManager(DbContext dbContext, Func, bool> filterFunc, Func createFunc, + public EfModelManager(DbContext dbContext, Func filterFunc, Func createFunc, ILoggerFactory loggerFactory) { _dbContext = dbContext ?? throw new ArgumentNullException("dbContext"); - _logger = loggerFactory.CreateLogger>(); + _logger = loggerFactory.CreateLogger>(); _defaultCreateFunc = createFunc; _filterFunc = filterFunc; } @@ -59,8 +60,8 @@ public EfModelManager(DbContext dbContext, Func>Function that describes how to sort data prior to get a portion /// Function that describes how to construct DTO from Model, if null passes here then uses _defaultCreateFunc /// OperationResult with data portion - public virtual async Task, long>>> GetManyAsync(int page, int size, IDictionary parameters, SortOption sorting, - Func, bool> filterFunc = null, + public virtual async Task, long>>> GetManyAsync(int page, int size, TFilter parameters, SortOption sorting, + Func filterFunc = null, Func sortFunc = null, Func createFunc = null) { try @@ -158,7 +159,7 @@ public virtual async Task> GetOneAsync(TId id, Funcraw query parameters /// OperationResult with data portion public virtual async Task, long>>> GetAsync(int page, int size, SortOption sorting = null, - IDictionary parameters = null) + TFilter parameters = null) { // this method is using default sorting and order, if specific order or sorting is required please specify it using another GetAsync method Func sortingFunc = null; @@ -265,9 +266,9 @@ public virtual Task> BulkDeleteAsync(TId[] objectsIds) throw new NotImplementedException(); } - private readonly ILogger> _logger; + private readonly ILogger> _logger; private readonly DbContext _dbContext; private readonly Func _defaultCreateFunc; - private readonly Func, bool> _filterFunc; + private readonly Func _filterFunc; } } diff --git a/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Managers/EfSoftRemovableModelManager.cs b/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Managers/EfSoftRemovableModelManager.cs index a3e2449..7bba7f9 100644 --- a/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Managers/EfSoftRemovableModelManager.cs +++ b/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Managers/EfSoftRemovableModelManager.cs @@ -13,24 +13,25 @@ namespace Wissance.WebApiToolkit.Managers { - public abstract class EfSoftRemovableModelManager : IModelManager + public abstract class EfSoftRemovableModelManager : IModelManager where TObj: class, IModelIdentifiable, IModelSoftRemovable where TRes: class where TId: IComparable + where TFilter: class { /// /// Constructor of default model manager requires that Model Context derives from EfDbContext /// /// Ef Database context /// Delegate (factory func) for creating DTO from Model - /// Function that use dictionary with query params to filter result set + /// Function that use TFilter with query params to filter result set /// Logger factory /// - public EfSoftRemovableModelManager(DbContext dbContext, Func, bool> filterFunc, Func createFunc, + public EfSoftRemovableModelManager(DbContext dbContext, Func filterFunc, Func createFunc, ILoggerFactory loggerFactory) { _dbContext = dbContext ?? throw new ArgumentNullException("dbContext"); - _logger = loggerFactory.CreateLogger>(); + _logger = loggerFactory.CreateLogger>(); _defaultCreateFunc = createFunc; _filterFunc = filterFunc; } @@ -48,8 +49,8 @@ public EfSoftRemovableModelManager(DbContext dbContext, Func>Function that describes how to sort data prior to get a portion /// Function that describes how to construct DTO from Model, if null passes here then uses _defaultCreateFunc /// OperationResult with data portion - public virtual async Task, long>>> GetManyAsync(int page, int size, IDictionary parameters, SortOption sorting, - Func, bool> filterFunc = null, + public virtual async Task, long>>> GetManyAsync(int page, int size, TFilter parameters, SortOption sorting, + Func filterFunc = null, Func sortFunc = null, Func createFunc = null) { try @@ -146,7 +147,7 @@ public virtual async Task> GetOneAsync(TId id, Funcraw query parameters /// OperationResult with data portion public virtual async Task, long>>> GetAsync(int page, int size, SortOption sorting = null, - IDictionary parameters = null) + TFilter parameters = null) { // this method is using default sorting and order, if specific order or sorting is required please specify it using another GetAsync method Func sortingFunc = null; @@ -253,9 +254,9 @@ public virtual Task> BulkDeleteAsync(TId[] objectsIds) throw new NotImplementedException(); } - private readonly ILogger> _logger; + private readonly ILogger> _logger; private readonly DbContext _dbContext; private readonly Func _defaultCreateFunc; - private readonly Func, bool> _filterFunc; + private readonly Func _filterFunc; } } \ No newline at end of file diff --git a/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Managers/IModelManager.cs b/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Managers/IModelManager.cs index 9ff3936..ae36e2f 100644 --- a/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Managers/IModelManager.cs +++ b/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Managers/IModelManager.cs @@ -17,7 +17,7 @@ namespace Wissance.WebApiToolkit.Managers /// TRes is a Result parameter which used as input and output for operation (DTO) /// Model type (Class that is mapping to PERSISTENT storage) /// Type of identifier, because IModelIdentifiable is a GENERIC - public interface IModelManager + public interface IModelManager where TFilter: class { /// /// Creates a new item in persistent storage (i.e. Database). To assign DTO to Model you should create a custom @@ -70,7 +70,7 @@ public interface IModelManager /// raw query parameters /// Task, long>>> GetAsync(int page, int size, SortOption sorting = null, - IDictionary parameters = null); + TFilter parameters = null); /// /// Return DTO representation of 1 object /// diff --git a/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Services/IResourceBasedCrudService.cs b/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Services/IResourceBasedCrudService.cs index e8cfff0..7bce966 100644 --- a/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Services/IResourceBasedCrudService.cs +++ b/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Services/IResourceBasedCrudService.cs @@ -12,8 +12,9 @@ namespace Wissance.WebApiToolkit.Services /// TRes (Resource) means Representation of Persistent data in external system i.e. DTO /// Persistent item type, in terms of Web App it is a Table or some ORM Entity Class /// Unique Identifier type (could be different for different apps i.e int/string/Guid) - public interface IResourceBasedCrudService : IResourceBasedReadOnlyService + public interface IResourceBasedCrudService : IResourceBasedReadOnlyService where TRes: class + where TFilter: class { Task> CreateAsync(TRes data); Task> UpdateAsync(TId id, TRes data); diff --git a/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Services/IResourceBasedReadOnlyService.cs b/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Services/IResourceBasedReadOnlyService.cs index c2563bc..1de0efd 100644 --- a/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Services/IResourceBasedReadOnlyService.cs +++ b/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Services/IResourceBasedReadOnlyService.cs @@ -12,10 +12,11 @@ namespace Wissance.WebApiToolkit.Services /// TRes (Resource) means Representation of Persistent data in external system i.e. DTO /// Persistent item type, in terms of Web App it is a Table or some ORM Entity Class /// Unique Identifier type (could be different for different apps i.e int/string/Guid) - public interface IResourceBasedReadOnlyService + public interface IResourceBasedReadOnlyService where TRes: class + where TFilter: class { - Task> ReadAsync(int? page, int? size, string sort, string order, IDictionary filterParams); + Task> ReadAsync(int? page, int? size, string sort, string order, TFilter filterParams); Task> ReadByIdAsync(TId id); } } \ No newline at end of file diff --git a/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Services/ResourceBasedDataManageableCrudService.cs b/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Services/ResourceBasedDataManageableCrudService.cs index 19c97af..7c6616e 100644 --- a/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Services/ResourceBasedDataManageableCrudService.cs +++ b/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Services/ResourceBasedDataManageableCrudService.cs @@ -11,9 +11,10 @@ namespace Wissance.WebApiToolkit.Services /// TRes (Resource) means Representation of Persistent data in external system i.e. DTO /// Persistent item type, in terms of Web App it is a Table or some ORM Entity Class /// Unique Identifier type (could be different for different apps i.e int/string/Guid) - public class ResourceBasedDataManageableCrudService : ResourceBasedDataManageableReadOnlyService, - IResourceBasedCrudService + public class ResourceBasedDataManageableCrudService : ResourceBasedDataManageableReadOnlyService, + IResourceBasedCrudService where TRes: class + where TFilter: class { public async Task> CreateAsync(TRes data) { diff --git a/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Services/ResourceBasedDataManageableReadOnlyService.cs b/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Services/ResourceBasedDataManageableReadOnlyService.cs index 2276b27..0864d28 100644 --- a/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Services/ResourceBasedDataManageableReadOnlyService.cs +++ b/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Services/ResourceBasedDataManageableReadOnlyService.cs @@ -18,10 +18,11 @@ namespace Wissance.WebApiToolkit.Services /// TRes (Resource) means Representation of Persistent data in external system i.e. DTO /// Persistent item type, in terms of Web App it is a Table or some ORM Entity Class /// Unique Identifier type (could be different for different apps i.e int/string/Guid) - public abstract class ResourceBasedDataManageableReadOnlyService : IResourceBasedReadOnlyService + public abstract class ResourceBasedDataManageableReadOnlyService : IResourceBasedReadOnlyService where TRes : class + where TFilter: class { - public virtual async Task> ReadAsync(int? page, int? size, string sort, string order, IDictionary filterParams) + public virtual async Task> ReadAsync(int? page, int? size, string sort, string order, TFilter filterParams) { int pageNumber = PagingUtils.GetPage(page); int pageSize = PagingUtils.GetPageSize(size); @@ -36,6 +37,6 @@ public virtual async Task> ReadByIdAsync(TId id) return result; } - public IModelManager Manager { get; set; } + public IModelManager Manager { get; set; } } } \ No newline at end of file From fcac79e6e4e035eb292a03a1bcc44524e7f54dd9 Mon Sep 17 00:00:00 2001 From: Alex Mamzikov Date: Tue, 7 May 2024 18:19:32 +0500 Subject: [PATCH 2/9] sort options is not nullable --- .../Controllers/BasicReadController.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Controllers/BasicReadController.cs b/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Controllers/BasicReadController.cs index 0e1e4ae..02360f2 100644 --- a/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Controllers/BasicReadController.cs +++ b/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Controllers/BasicReadController.cs @@ -11,17 +11,17 @@ namespace Wissance.WebApiToolkit.Controllers { public abstract class BasicReadController : BasicPagedDataController - where TRes: class - where TFilter: class + where TRes : class + where TFilter : class { [HttpGet] [Route("api/[controller]")] - public virtual async Task> ReadAsync([FromQuery] int? page, [FromQuery] int? size, [FromQuery] string sort, - [FromQuery] string order, TFilter additionalFilters = null) + public virtual async Task> ReadAsync([FromQuery] int? page, [FromQuery] int? size, [FromQuery] string sort, + [FromQuery] string order, TFilter additionalFilters) { int pageNumber = GetPage(page); int pageSize = GetPageSize(size); - SortOption sorting = !string.IsNullOrEmpty(sort) ? new SortOption(sort, order) : null; + SortOption sorting = new SortOption(sort ?? "", order ?? ""); OperationResultDto, long>> result = await Manager.GetAsync(pageNumber, pageSize, sorting, additionalFilters); HttpContext.Response.StatusCode = result.Status; return new PagedDataDto(pageNumber, result.Data.Item2, PagingUtils.GetTotalPages(result.Data.Item2, pageSize), result.Data.Item1); From c737e05c842cac7fa0aed563c0991aeab532729c Mon Sep 17 00:00:00 2001 From: Alex Mamzikov Date: Tue, 7 May 2024 18:21:08 +0500 Subject: [PATCH 3/9] nullable sort options is reverted --- .../Wissance.WebApiToolkit/Controllers/BasicReadController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Controllers/BasicReadController.cs b/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Controllers/BasicReadController.cs index 02360f2..2209879 100644 --- a/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Controllers/BasicReadController.cs +++ b/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Controllers/BasicReadController.cs @@ -21,7 +21,7 @@ public virtual async Task> ReadAsync([FromQuery] int? page, [ { int pageNumber = GetPage(page); int pageSize = GetPageSize(size); - SortOption sorting = new SortOption(sort ?? "", order ?? ""); + SortOption sorting = !string.IsNullOrEmpty(sort) ? new SortOption(sort, order) : null; OperationResultDto, long>> result = await Manager.GetAsync(pageNumber, pageSize, sorting, additionalFilters); HttpContext.Response.StatusCode = result.Status; return new PagedDataDto(pageNumber, result.Data.Item2, PagingUtils.GetTotalPages(result.Data.Item2, pageSize), result.Data.Item1); From f11cb4af8ca972defa884fcca6dc34f577793e82 Mon Sep 17 00:00:00 2001 From: Alex Mamzikov Date: Tue, 7 May 2024 18:59:57 +0500 Subject: [PATCH 4/9] documentation is added --- .../Controllers/BasicCrudController.cs | 18 ++++++++++++++++-- .../Controllers/BasicReadController.cs | 16 +++++++++++++++- .../Wissance.WebApiToolkit.csproj | 2 ++ 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Controllers/BasicCrudController.cs b/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Controllers/BasicCrudController.cs index 48b6c0b..51367e0 100644 --- a/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Controllers/BasicCrudController.cs +++ b/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Controllers/BasicCrudController.cs @@ -1,8 +1,6 @@ using System; -using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; -using Wissance.WebApiToolkit.Data; using Wissance.WebApiToolkit.Dto; namespace Wissance.WebApiToolkit.Controllers @@ -11,6 +9,11 @@ public abstract class BasicCrudController : BasicRea where TRes : class where TFilter: class { + /// + /// Creates a new object + /// + /// The new object + /// [HttpPost] [Route("api/[controller]")] public virtual async Task> CreateAsync([FromBody] TRes data) @@ -20,6 +23,12 @@ public virtual async Task> CreateAsync([FromBody] TRes return result; } + /// + /// Updates an object + /// + /// Identifier of an object to update + /// The object data + /// [HttpPut] [Route("api/[controller]/{id}")] public virtual async Task> UpdateAsync([FromRoute] TId id, [FromBody] TRes data) @@ -29,6 +38,11 @@ public virtual async Task> UpdateAsync([FromRoute] TId return result; } + /// + /// Deletes an object by identifier + /// + /// Identifier of the object to delete + /// [HttpDelete] [Route("api/[controller]/{id}")] public virtual async Task DeleteAsync([FromRoute] TId id) diff --git a/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Controllers/BasicReadController.cs b/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Controllers/BasicReadController.cs index 2209879..3173dd1 100644 --- a/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Controllers/BasicReadController.cs +++ b/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Controllers/BasicReadController.cs @@ -14,10 +14,19 @@ public abstract class BasicReadController : BasicPage where TRes : class where TFilter : class { + /// + /// Returns a list of objects + /// + /// Page number + /// Page size + /// Field name for sorting by it + /// Sorting order: asc or desc + /// + /// [HttpGet] [Route("api/[controller]")] public virtual async Task> ReadAsync([FromQuery] int? page, [FromQuery] int? size, [FromQuery] string sort, - [FromQuery] string order, TFilter additionalFilters) + [FromQuery] string order, [FromQuery] TFilter additionalFilters) { int pageNumber = GetPage(page); int pageSize = GetPageSize(size); @@ -27,6 +36,11 @@ public virtual async Task> ReadAsync([FromQuery] int? page, [ return new PagedDataDto(pageNumber, result.Data.Item2, PagingUtils.GetTotalPages(result.Data.Item2, pageSize), result.Data.Item1); } + /// + /// Returns one object by identifier + /// + /// Identifier of the object + /// [HttpGet] [Route("api/[controller]/{id}")] public async Task ReadByIdAsync([FromRoute] TId id) diff --git a/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Wissance.WebApiToolkit.csproj b/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Wissance.WebApiToolkit.csproj index b1e5019..0628dd0 100644 --- a/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Wissance.WebApiToolkit.csproj +++ b/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Wissance.WebApiToolkit.csproj @@ -2,6 +2,8 @@ net5.0;net6.0;netcoreapp3.1;net8.0 + true + $(NoWarn);1591 From 7c70dcfd8eac47e36f451362e1088158a068c77f Mon Sep 17 00:00:00 2001 From: Alex Mamzikov Date: Tue, 7 May 2024 19:52:47 +0500 Subject: [PATCH 5/9] EmptyAdditionalFilters.cs is restored --- .../Wissance.WebApiToolkit/Data/EmptyAdditionalFilters.cs | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 Wissance.WebApiToolkit/Wissance.WebApiToolkit/Data/EmptyAdditionalFilters.cs diff --git a/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Data/EmptyAdditionalFilters.cs b/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Data/EmptyAdditionalFilters.cs new file mode 100644 index 0000000..e2a78a3 --- /dev/null +++ b/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Data/EmptyAdditionalFilters.cs @@ -0,0 +1,6 @@ +namespace Wissance.WebApiToolkit.Data +{ + public class EmptyAdditionalFilters + { + } +} \ No newline at end of file From 1f925827f95201db1641ea59ab102aaf29c9a6bd Mon Sep 17 00:00:00 2001 From: Alex Mamzikov Date: Wed, 8 May 2024 12:45:26 +0500 Subject: [PATCH 6/9] typeparam TFilter is added to the documentation --- .../Wissance.WebApiToolkit/Managers/EfModelManager.cs | 1 + .../Wissance.WebApiToolkit/Managers/IModelManager.cs | 1 + .../Wissance.WebApiToolkit/Services/IResourceBasedCrudService.cs | 1 + .../Services/ResourceBasedDataManageableCrudService.cs | 1 + .../Services/ResourceBasedDataManageableReadOnlyService.cs | 1 + 5 files changed, 5 insertions(+) diff --git a/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Managers/EfModelManager.cs b/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Managers/EfModelManager.cs index 183208f..17ed3e3 100644 --- a/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Managers/EfModelManager.cs +++ b/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Managers/EfModelManager.cs @@ -23,6 +23,7 @@ namespace Wissance.WebApiToolkit.Managers /// Model class implements IModelIdentifiable /// DTO class (representation of Model in other systems i.e. in frontend)) /// Identifier type that is using as database table PK + /// Type of arguments with fields marked by FromQuery attribute public abstract class EfModelManager : IModelManager where TObj: class, IModelIdentifiable where TRes: class diff --git a/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Managers/IModelManager.cs b/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Managers/IModelManager.cs index ae36e2f..1600877 100644 --- a/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Managers/IModelManager.cs +++ b/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Managers/IModelManager.cs @@ -17,6 +17,7 @@ namespace Wissance.WebApiToolkit.Managers /// TRes is a Result parameter which used as input and output for operation (DTO) /// Model type (Class that is mapping to PERSISTENT storage) /// Type of identifier, because IModelIdentifiable is a GENERIC + /// Type of arguments with fields marked by FromQuery attribute public interface IModelManager where TFilter: class { /// diff --git a/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Services/IResourceBasedCrudService.cs b/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Services/IResourceBasedCrudService.cs index 7bce966..d698baf 100644 --- a/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Services/IResourceBasedCrudService.cs +++ b/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Services/IResourceBasedCrudService.cs @@ -12,6 +12,7 @@ namespace Wissance.WebApiToolkit.Services /// TRes (Resource) means Representation of Persistent data in external system i.e. DTO /// Persistent item type, in terms of Web App it is a Table or some ORM Entity Class /// Unique Identifier type (could be different for different apps i.e int/string/Guid) + /// Type of arguments with fields marked by FromQuery attribute public interface IResourceBasedCrudService : IResourceBasedReadOnlyService where TRes: class where TFilter: class diff --git a/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Services/ResourceBasedDataManageableCrudService.cs b/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Services/ResourceBasedDataManageableCrudService.cs index 7c6616e..c7a378c 100644 --- a/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Services/ResourceBasedDataManageableCrudService.cs +++ b/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Services/ResourceBasedDataManageableCrudService.cs @@ -11,6 +11,7 @@ namespace Wissance.WebApiToolkit.Services /// TRes (Resource) means Representation of Persistent data in external system i.e. DTO /// Persistent item type, in terms of Web App it is a Table or some ORM Entity Class /// Unique Identifier type (could be different for different apps i.e int/string/Guid) + /// Type of arguments with fields marked by FromQuery attribute public class ResourceBasedDataManageableCrudService : ResourceBasedDataManageableReadOnlyService, IResourceBasedCrudService where TRes: class diff --git a/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Services/ResourceBasedDataManageableReadOnlyService.cs b/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Services/ResourceBasedDataManageableReadOnlyService.cs index 0864d28..8142131 100644 --- a/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Services/ResourceBasedDataManageableReadOnlyService.cs +++ b/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Services/ResourceBasedDataManageableReadOnlyService.cs @@ -18,6 +18,7 @@ namespace Wissance.WebApiToolkit.Services /// TRes (Resource) means Representation of Persistent data in external system i.e. DTO /// Persistent item type, in terms of Web App it is a Table or some ORM Entity Class /// Unique Identifier type (could be different for different apps i.e int/string/Guid) + /// Type of arguments with fields marked by FromQuery attribute public abstract class ResourceBasedDataManageableReadOnlyService : IResourceBasedReadOnlyService where TRes : class where TFilter: class From 6d42f9173ae75ba37365a8a676baff676c5d14fb Mon Sep 17 00:00:00 2001 From: Ushakov Michale Date: Fri, 11 Oct 2024 23:43:14 +0500 Subject: [PATCH 7/9] Revert "Merge pull request #26 from av-mamzikov/feature/types_arguments" This reverts commit c211c8c38ff92f8cf4bce712853fba42184c38b5, reversing changes made to 79b0c510458ad67b249df2970c17e083d2170b9c. --- .../Controllers/BasicBulkCrudController.cs | 2 +- .../Controllers/BasicCrudController.cs | 20 ++---------- .../Controllers/BasicReadController.cs | 32 +++++++------------ .../Data/EmptyAdditionalFilters.cs | 8 ++++- .../Data/IReadFilterable.cs | 9 ++++++ .../Managers/EfModelManager.cs | 20 ++++++------ .../Managers/EfSoftRemovableModelManager.cs | 19 ++++++----- .../Managers/IModelManager.cs | 5 ++- .../Services/IResourceBasedCrudService.cs | 4 +-- .../Services/IResourceBasedReadOnlyService.cs | 5 ++- .../ResourceBasedDataManageableCrudService.cs | 6 ++-- ...ourceBasedDataManageableReadOnlyService.cs | 8 ++--- .../Wissance.WebApiToolkit.csproj | 2 -- 13 files changed, 60 insertions(+), 80 deletions(-) create mode 100644 Wissance.WebApiToolkit/Wissance.WebApiToolkit/Data/IReadFilterable.cs diff --git a/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Controllers/BasicBulkCrudController.cs b/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Controllers/BasicBulkCrudController.cs index 544f966..e9524f9 100644 --- a/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Controllers/BasicBulkCrudController.cs +++ b/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Controllers/BasicBulkCrudController.cs @@ -7,7 +7,7 @@ namespace Wissance.WebApiToolkit.Controllers { public class BasicBulkCrudController : BasicReadController where TRes : class - where TFilter: class + where TFilter: class, IReadFilterable { [HttpPost] [Route("api/bulk/[controller]")] diff --git a/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Controllers/BasicCrudController.cs b/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Controllers/BasicCrudController.cs index 51367e0..7af5527 100644 --- a/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Controllers/BasicCrudController.cs +++ b/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Controllers/BasicCrudController.cs @@ -1,19 +1,16 @@ using System; +using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; +using Wissance.WebApiToolkit.Data; using Wissance.WebApiToolkit.Dto; namespace Wissance.WebApiToolkit.Controllers { public abstract class BasicCrudController : BasicReadController where TRes : class - where TFilter: class + where TFilter: class, IReadFilterable { - /// - /// Creates a new object - /// - /// The new object - /// [HttpPost] [Route("api/[controller]")] public virtual async Task> CreateAsync([FromBody] TRes data) @@ -23,12 +20,6 @@ public virtual async Task> CreateAsync([FromBody] TRes return result; } - /// - /// Updates an object - /// - /// Identifier of an object to update - /// The object data - /// [HttpPut] [Route("api/[controller]/{id}")] public virtual async Task> UpdateAsync([FromRoute] TId id, [FromBody] TRes data) @@ -38,11 +29,6 @@ public virtual async Task> UpdateAsync([FromRoute] TId return result; } - /// - /// Deletes an object by identifier - /// - /// Identifier of the object to delete - /// [HttpDelete] [Route("api/[controller]/{id}")] public virtual async Task DeleteAsync([FromRoute] TId id) diff --git a/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Controllers/BasicReadController.cs b/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Controllers/BasicReadController.cs index 3173dd1..bbf1e6e 100644 --- a/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Controllers/BasicReadController.cs +++ b/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Controllers/BasicReadController.cs @@ -1,7 +1,10 @@ using System; +using System.Collections; using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Primitives; using Wissance.WebApiToolkit.Data; using Wissance.WebApiToolkit.Dto; using Wissance.WebApiToolkit.Managers; @@ -11,36 +14,25 @@ namespace Wissance.WebApiToolkit.Controllers { public abstract class BasicReadController : BasicPagedDataController - where TRes : class - where TFilter : class + where TRes: class + where TFilter: class, IReadFilterable { - /// - /// Returns a list of objects - /// - /// Page number - /// Page size - /// Field name for sorting by it - /// Sorting order: asc or desc - /// - /// [HttpGet] [Route("api/[controller]")] - public virtual async Task> ReadAsync([FromQuery] int? page, [FromQuery] int? size, [FromQuery] string sort, - [FromQuery] string order, [FromQuery] TFilter additionalFilters) + public virtual async Task> ReadAsync([FromQuery] int? page, [FromQuery] int? size, [FromQuery] string sort, + [FromQuery] string order, TFilter additionalFilters = null) { int pageNumber = GetPage(page); int pageSize = GetPageSize(size); SortOption sorting = !string.IsNullOrEmpty(sort) ? new SortOption(sort, order) : null; - OperationResultDto, long>> result = await Manager.GetAsync(pageNumber, pageSize, sorting, additionalFilters); + IDictionary additionalQueryParams = additionalFilters != null + ? additionalFilters.SelectFilters() + : new Dictionary(); + OperationResultDto, long>> result = await Manager.GetAsync(pageNumber, pageSize, sorting, additionalQueryParams); HttpContext.Response.StatusCode = result.Status; return new PagedDataDto(pageNumber, result.Data.Item2, PagingUtils.GetTotalPages(result.Data.Item2, pageSize), result.Data.Item1); } - /// - /// Returns one object by identifier - /// - /// Identifier of the object - /// [HttpGet] [Route("api/[controller]/{id}")] public async Task ReadByIdAsync([FromRoute] TId id) @@ -49,6 +41,6 @@ public async Task ReadByIdAsync([FromRoute] TId id) HttpContext.Response.StatusCode = result.Status; return result.Data; } - public IModelManager Manager { get; set; } + public IModelManager Manager { get; set; } } } diff --git a/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Data/EmptyAdditionalFilters.cs b/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Data/EmptyAdditionalFilters.cs index e2a78a3..fffdf39 100644 --- a/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Data/EmptyAdditionalFilters.cs +++ b/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Data/EmptyAdditionalFilters.cs @@ -1,6 +1,12 @@ +using System.Collections.Generic; + namespace Wissance.WebApiToolkit.Data { - public class EmptyAdditionalFilters + public class EmptyAdditionalFilters : IReadFilterable { + public IDictionary SelectFilters() + { + return new Dictionary(); + } } } \ No newline at end of file diff --git a/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Data/IReadFilterable.cs b/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Data/IReadFilterable.cs new file mode 100644 index 0000000..1fcdbfc --- /dev/null +++ b/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Data/IReadFilterable.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; + +namespace Wissance.WebApiToolkit.Data +{ + public interface IReadFilterable + { + IDictionary SelectFilters(); + } +} \ No newline at end of file diff --git a/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Managers/EfModelManager.cs b/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Managers/EfModelManager.cs index 17ed3e3..72a5383 100644 --- a/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Managers/EfModelManager.cs +++ b/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Managers/EfModelManager.cs @@ -23,12 +23,10 @@ namespace Wissance.WebApiToolkit.Managers /// Model class implements IModelIdentifiable /// DTO class (representation of Model in other systems i.e. in frontend)) /// Identifier type that is using as database table PK - /// Type of arguments with fields marked by FromQuery attribute - public abstract class EfModelManager : IModelManager + public abstract class EfModelManager : IModelManager where TObj: class, IModelIdentifiable where TRes: class where TId: IComparable - where TFilter: class { /// @@ -36,14 +34,14 @@ public abstract class EfModelManager : IModelManager< /// /// Ef Database context /// Delegate (factory func) for creating DTO from Model - /// Function that use TFilter with query params to filter result set + /// Function that use dictionary with query params to filter result set /// Logger factory /// - public EfModelManager(DbContext dbContext, Func filterFunc, Func createFunc, + public EfModelManager(DbContext dbContext, Func, bool> filterFunc, Func createFunc, ILoggerFactory loggerFactory) { _dbContext = dbContext ?? throw new ArgumentNullException("dbContext"); - _logger = loggerFactory.CreateLogger>(); + _logger = loggerFactory.CreateLogger>(); _defaultCreateFunc = createFunc; _filterFunc = filterFunc; } @@ -61,8 +59,8 @@ public EfModelManager(DbContext dbContext, Func filterFunc, /// >Function that describes how to sort data prior to get a portion /// Function that describes how to construct DTO from Model, if null passes here then uses _defaultCreateFunc /// OperationResult with data portion - public virtual async Task, long>>> GetManyAsync(int page, int size, TFilter parameters, SortOption sorting, - Func filterFunc = null, + public virtual async Task, long>>> GetManyAsync(int page, int size, IDictionary parameters, SortOption sorting, + Func, bool> filterFunc = null, Func sortFunc = null, Func createFunc = null) { try @@ -160,7 +158,7 @@ public virtual async Task> GetOneAsync(TId id, Funcraw query parameters /// OperationResult with data portion public virtual async Task, long>>> GetAsync(int page, int size, SortOption sorting = null, - TFilter parameters = null) + IDictionary parameters = null) { // this method is using default sorting and order, if specific order or sorting is required please specify it using another GetAsync method Func sortingFunc = null; @@ -267,9 +265,9 @@ public virtual Task> BulkDeleteAsync(TId[] objectsIds) throw new NotImplementedException(); } - private readonly ILogger> _logger; + private readonly ILogger> _logger; private readonly DbContext _dbContext; private readonly Func _defaultCreateFunc; - private readonly Func _filterFunc; + private readonly Func, bool> _filterFunc; } } diff --git a/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Managers/EfSoftRemovableModelManager.cs b/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Managers/EfSoftRemovableModelManager.cs index 7bba7f9..a3e2449 100644 --- a/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Managers/EfSoftRemovableModelManager.cs +++ b/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Managers/EfSoftRemovableModelManager.cs @@ -13,25 +13,24 @@ namespace Wissance.WebApiToolkit.Managers { - public abstract class EfSoftRemovableModelManager : IModelManager + public abstract class EfSoftRemovableModelManager : IModelManager where TObj: class, IModelIdentifiable, IModelSoftRemovable where TRes: class where TId: IComparable - where TFilter: class { /// /// Constructor of default model manager requires that Model Context derives from EfDbContext /// /// Ef Database context /// Delegate (factory func) for creating DTO from Model - /// Function that use TFilter with query params to filter result set + /// Function that use dictionary with query params to filter result set /// Logger factory /// - public EfSoftRemovableModelManager(DbContext dbContext, Func filterFunc, Func createFunc, + public EfSoftRemovableModelManager(DbContext dbContext, Func, bool> filterFunc, Func createFunc, ILoggerFactory loggerFactory) { _dbContext = dbContext ?? throw new ArgumentNullException("dbContext"); - _logger = loggerFactory.CreateLogger>(); + _logger = loggerFactory.CreateLogger>(); _defaultCreateFunc = createFunc; _filterFunc = filterFunc; } @@ -49,8 +48,8 @@ public EfSoftRemovableModelManager(DbContext dbContext, Func>Function that describes how to sort data prior to get a portion /// Function that describes how to construct DTO from Model, if null passes here then uses _defaultCreateFunc /// OperationResult with data portion - public virtual async Task, long>>> GetManyAsync(int page, int size, TFilter parameters, SortOption sorting, - Func filterFunc = null, + public virtual async Task, long>>> GetManyAsync(int page, int size, IDictionary parameters, SortOption sorting, + Func, bool> filterFunc = null, Func sortFunc = null, Func createFunc = null) { try @@ -147,7 +146,7 @@ public virtual async Task> GetOneAsync(TId id, Funcraw query parameters /// OperationResult with data portion public virtual async Task, long>>> GetAsync(int page, int size, SortOption sorting = null, - TFilter parameters = null) + IDictionary parameters = null) { // this method is using default sorting and order, if specific order or sorting is required please specify it using another GetAsync method Func sortingFunc = null; @@ -254,9 +253,9 @@ public virtual Task> BulkDeleteAsync(TId[] objectsIds) throw new NotImplementedException(); } - private readonly ILogger> _logger; + private readonly ILogger> _logger; private readonly DbContext _dbContext; private readonly Func _defaultCreateFunc; - private readonly Func _filterFunc; + private readonly Func, bool> _filterFunc; } } \ No newline at end of file diff --git a/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Managers/IModelManager.cs b/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Managers/IModelManager.cs index 1600877..9ff3936 100644 --- a/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Managers/IModelManager.cs +++ b/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Managers/IModelManager.cs @@ -17,8 +17,7 @@ namespace Wissance.WebApiToolkit.Managers /// TRes is a Result parameter which used as input and output for operation (DTO) /// Model type (Class that is mapping to PERSISTENT storage) /// Type of identifier, because IModelIdentifiable is a GENERIC - /// Type of arguments with fields marked by FromQuery attribute - public interface IModelManager where TFilter: class + public interface IModelManager { /// /// Creates a new item in persistent storage (i.e. Database). To assign DTO to Model you should create a custom @@ -71,7 +70,7 @@ public interface IModelManager where TFilter: class /// raw query parameters /// Task, long>>> GetAsync(int page, int size, SortOption sorting = null, - TFilter parameters = null); + IDictionary parameters = null); /// /// Return DTO representation of 1 object /// diff --git a/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Services/IResourceBasedCrudService.cs b/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Services/IResourceBasedCrudService.cs index d698baf..e8cfff0 100644 --- a/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Services/IResourceBasedCrudService.cs +++ b/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Services/IResourceBasedCrudService.cs @@ -12,10 +12,8 @@ namespace Wissance.WebApiToolkit.Services /// TRes (Resource) means Representation of Persistent data in external system i.e. DTO /// Persistent item type, in terms of Web App it is a Table or some ORM Entity Class /// Unique Identifier type (could be different for different apps i.e int/string/Guid) - /// Type of arguments with fields marked by FromQuery attribute - public interface IResourceBasedCrudService : IResourceBasedReadOnlyService + public interface IResourceBasedCrudService : IResourceBasedReadOnlyService where TRes: class - where TFilter: class { Task> CreateAsync(TRes data); Task> UpdateAsync(TId id, TRes data); diff --git a/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Services/IResourceBasedReadOnlyService.cs b/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Services/IResourceBasedReadOnlyService.cs index 1de0efd..c2563bc 100644 --- a/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Services/IResourceBasedReadOnlyService.cs +++ b/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Services/IResourceBasedReadOnlyService.cs @@ -12,11 +12,10 @@ namespace Wissance.WebApiToolkit.Services /// TRes (Resource) means Representation of Persistent data in external system i.e. DTO /// Persistent item type, in terms of Web App it is a Table or some ORM Entity Class /// Unique Identifier type (could be different for different apps i.e int/string/Guid) - public interface IResourceBasedReadOnlyService + public interface IResourceBasedReadOnlyService where TRes: class - where TFilter: class { - Task> ReadAsync(int? page, int? size, string sort, string order, TFilter filterParams); + Task> ReadAsync(int? page, int? size, string sort, string order, IDictionary filterParams); Task> ReadByIdAsync(TId id); } } \ No newline at end of file diff --git a/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Services/ResourceBasedDataManageableCrudService.cs b/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Services/ResourceBasedDataManageableCrudService.cs index c7a378c..19c97af 100644 --- a/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Services/ResourceBasedDataManageableCrudService.cs +++ b/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Services/ResourceBasedDataManageableCrudService.cs @@ -11,11 +11,9 @@ namespace Wissance.WebApiToolkit.Services /// TRes (Resource) means Representation of Persistent data in external system i.e. DTO /// Persistent item type, in terms of Web App it is a Table or some ORM Entity Class /// Unique Identifier type (could be different for different apps i.e int/string/Guid) - /// Type of arguments with fields marked by FromQuery attribute - public class ResourceBasedDataManageableCrudService : ResourceBasedDataManageableReadOnlyService, - IResourceBasedCrudService + public class ResourceBasedDataManageableCrudService : ResourceBasedDataManageableReadOnlyService, + IResourceBasedCrudService where TRes: class - where TFilter: class { public async Task> CreateAsync(TRes data) { diff --git a/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Services/ResourceBasedDataManageableReadOnlyService.cs b/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Services/ResourceBasedDataManageableReadOnlyService.cs index 8142131..2276b27 100644 --- a/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Services/ResourceBasedDataManageableReadOnlyService.cs +++ b/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Services/ResourceBasedDataManageableReadOnlyService.cs @@ -18,12 +18,10 @@ namespace Wissance.WebApiToolkit.Services /// TRes (Resource) means Representation of Persistent data in external system i.e. DTO /// Persistent item type, in terms of Web App it is a Table or some ORM Entity Class /// Unique Identifier type (could be different for different apps i.e int/string/Guid) - /// Type of arguments with fields marked by FromQuery attribute - public abstract class ResourceBasedDataManageableReadOnlyService : IResourceBasedReadOnlyService + public abstract class ResourceBasedDataManageableReadOnlyService : IResourceBasedReadOnlyService where TRes : class - where TFilter: class { - public virtual async Task> ReadAsync(int? page, int? size, string sort, string order, TFilter filterParams) + public virtual async Task> ReadAsync(int? page, int? size, string sort, string order, IDictionary filterParams) { int pageNumber = PagingUtils.GetPage(page); int pageSize = PagingUtils.GetPageSize(size); @@ -38,6 +36,6 @@ public virtual async Task> ReadByIdAsync(TId id) return result; } - public IModelManager Manager { get; set; } + public IModelManager Manager { get; set; } } } \ No newline at end of file diff --git a/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Wissance.WebApiToolkit.csproj b/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Wissance.WebApiToolkit.csproj index 0628dd0..b1e5019 100644 --- a/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Wissance.WebApiToolkit.csproj +++ b/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Wissance.WebApiToolkit.csproj @@ -2,8 +2,6 @@ net5.0;net6.0;netcoreapp3.1;net8.0 - true - $(NoWarn);1591 From 7c58dedcf74e53679840dcce50fcd16edb10a65e Mon Sep 17 00:00:00 2001 From: Ushakov Michale Date: Sun, 13 Oct 2024 20:28:05 +0500 Subject: [PATCH 8/9] docs + types reorder --- .../Controllers/BasicReadController.cs | 24 +++++++++++++++++++ .../Managers/EfModelManager.cs | 8 +++---- .../Managers/EfSoftRemovableModelManager.cs | 6 ++--- 3 files changed, 31 insertions(+), 7 deletions(-) diff --git a/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Controllers/BasicReadController.cs b/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Controllers/BasicReadController.cs index bbf1e6e..e357846 100644 --- a/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Controllers/BasicReadController.cs +++ b/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Controllers/BasicReadController.cs @@ -13,6 +13,30 @@ namespace Wissance.WebApiToolkit.Controllers { + /// + /// This is a basic Read Controller implementing two operations: + /// 1. Get multiple items via api/controller/[page={20} & size={50} & sort=name & order=asc|desc ] with paging + /// and sorting by one column and filtering by any number of parameters, sort is a name of column/property + /// order is a sort order only asc and desc values are allowed. + /// Examples with Station controller: + /// - ~/api/Station to get items with default paging (page = 1, size = 25) + /// - ~/api/Station?page=2&size=50 to get items with paging options (page = 2, size = 50) + /// - ~/api/Station?page=1&size=40&sort=name&order=desc to get items with paging and sorting by column/property + /// name and in desc order (from Z to A) + /// - ~/api/Station?page=1&size=40&sort=name&order=desc&from=2022-01-01&to=2024-12-31 to get items with paging, + /// sorting and filter by date in range (2022-01-01, 2024-12-31) + /// 2. Get single item via api by id, example with Station controller: + /// - ~/api/Station/145 to get station with id = 145 + /// Restrictions: + /// 1. We are working with a single type of result representation inn Response (TRes). + /// 2. To get custom filters there should be a class implementing IReadFilter, you could just return an + /// empty dictionary and act with TFilter on you own way. + /// 3. There are no option (currently) to receive all data without paging + /// + /// Is A DTO object type to return back + /// Is a type that represents persistent object (i.e. Entity class) + /// Is a type of persistant object identifier + /// Is a type of filter class public abstract class BasicReadController : BasicPagedDataController where TRes: class where TFilter: class, IReadFilterable diff --git a/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Managers/EfModelManager.cs b/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Managers/EfModelManager.cs index 72a5383..cae2d4a 100644 --- a/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Managers/EfModelManager.cs +++ b/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Managers/EfModelManager.cs @@ -20,10 +20,10 @@ namespace Wissance.WebApiToolkit.Managers /// * GetByIdAsync method for obtain one item by id /// * Delete method /// - /// Model class implements IModelIdentifiable /// DTO class (representation of Model in other systems i.e. in frontend)) + /// Model class implements IModelIdentifiable /// Identifier type that is using as database table PK - public abstract class EfModelManager : IModelManager + public abstract class EfModelManager : IModelManager where TObj: class, IModelIdentifiable where TRes: class where TId: IComparable @@ -41,7 +41,7 @@ public EfModelManager(DbContext dbContext, Func>(); + _logger = loggerFactory.CreateLogger>(); _defaultCreateFunc = createFunc; _filterFunc = filterFunc; } @@ -265,7 +265,7 @@ public virtual Task> BulkDeleteAsync(TId[] objectsIds) throw new NotImplementedException(); } - private readonly ILogger> _logger; + private readonly ILogger> _logger; private readonly DbContext _dbContext; private readonly Func _defaultCreateFunc; private readonly Func, bool> _filterFunc; diff --git a/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Managers/EfSoftRemovableModelManager.cs b/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Managers/EfSoftRemovableModelManager.cs index a3e2449..c7ed990 100644 --- a/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Managers/EfSoftRemovableModelManager.cs +++ b/Wissance.WebApiToolkit/Wissance.WebApiToolkit/Managers/EfSoftRemovableModelManager.cs @@ -13,7 +13,7 @@ namespace Wissance.WebApiToolkit.Managers { - public abstract class EfSoftRemovableModelManager : IModelManager + public abstract class EfSoftRemovableModelManager : IModelManager where TObj: class, IModelIdentifiable, IModelSoftRemovable where TRes: class where TId: IComparable @@ -30,7 +30,7 @@ public EfSoftRemovableModelManager(DbContext dbContext, Func>(); + _logger = loggerFactory.CreateLogger>(); _defaultCreateFunc = createFunc; _filterFunc = filterFunc; } @@ -253,7 +253,7 @@ public virtual Task> BulkDeleteAsync(TId[] objectsIds) throw new NotImplementedException(); } - private readonly ILogger> _logger; + private readonly ILogger> _logger; private readonly DbContext _dbContext; private readonly Func _defaultCreateFunc; private readonly Func, bool> _filterFunc; From 23df5317492c8826755fa946b712c2022a76c795 Mon Sep 17 00:00:00 2001 From: Ushakov Michale Date: Sun, 13 Oct 2024 20:33:55 +0500 Subject: [PATCH 9/9] Issue #6 types reorder --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7306c16..51f1ed3 100644 --- a/README.md +++ b/README.md @@ -130,7 +130,7 @@ public class BookController : BasicCrudController +public class BookManager : EfModelManager { public BookManager(ModelContext modelContext, ILoggerFactory loggerFactory) : base(modelContext, BookFactory.Create, loggerFactory) { @@ -200,7 +200,7 @@ public class StationController : BasicCrudController +public class StationManager : EfModelManager { public StationManager(ModelContext modelContext, ILoggerFactory loggerFactory) : base(modelContext, StationFactory.Create, loggerFactory) {