diff --git a/README.md b/README.md index f212a00..4048fcb 100644 --- a/README.md +++ b/README.md @@ -118,7 +118,7 @@ public class OrderMapper : IMapper, IMapper +public interface IOrderService : ICrudService { } @@ -182,6 +182,6 @@ or ```csharp ... -app.MapAllCrud(assembly); +app.MapAllCrud(opt => opt.FromAssembly(assembly)); app.Run(); ``` \ No newline at end of file diff --git a/src/eQuantic.Core.Api.Crud/Controllers/CrudControllerBase.cs b/src/eQuantic.Core.Api.Crud/Controllers/CrudControllerBase.cs index 225a546..fd3bd56 100644 --- a/src/eQuantic.Core.Api.Crud/Controllers/CrudControllerBase.cs +++ b/src/eQuantic.Core.Api.Crud/Controllers/CrudControllerBase.cs @@ -13,13 +13,13 @@ namespace eQuantic.Core.Api.Crud.Controllers; /// public abstract class CrudControllerBase : ControllerBase, ICrudController where TEntity : class, new() { - private readonly ICrudServiceBase _service; + private readonly ICrudService _service; /// /// Initializes a new instance of the class /// /// - protected CrudControllerBase(ICrudServiceBase service) + protected CrudControllerBase(ICrudService service) { _service = service; } diff --git a/src/eQuantic.Core.Api.Crud/Extensions/WepApplicationExtensions.cs b/src/eQuantic.Core.Api.Crud/Extensions/WepApplicationExtensions.cs index 6d4ad46..cf05b67 100644 --- a/src/eQuantic.Core.Api.Crud/Extensions/WepApplicationExtensions.cs +++ b/src/eQuantic.Core.Api.Crud/Extensions/WepApplicationExtensions.cs @@ -20,16 +20,20 @@ public static class WepApplicationExtensions /// Map all CRUD endpoints /// /// - /// + /// Map all CRUD options /// - public static WebApplication MapAllCrud(this WebApplication app, Assembly assembly) + public static WebApplication MapAllCrud(this WebApplication app, Action? options = null) { + var allCrudOptions = new AllCrudOptions(); + options?.Invoke(allCrudOptions); + var extensionType = typeof(WepApplicationExtensions); + var assembly = allCrudOptions.GetAssembly() ?? Assembly.GetExecutingAssembly(); var types = assembly.GetTypes() .Where(o => o is { IsAbstract: false, IsInterface: false } && - o.GetInterfaces().Any(i => i == typeof(ICrudServiceBase)) && + o.GetInterfaces().Any(i => i == typeof(ICrudService)) && o.GetCustomAttribute() != null); foreach (var type in types) @@ -45,7 +49,7 @@ public static WebApplication MapAllCrud(this WebApplication app, Assembly assemb var crudInterface = interfaces .FirstOrDefault(o => - o.GenericTypeArguments.Length > 0 && o.GetGenericTypeDefinition() == typeof(ICrudServiceBase<,>)); + o.GenericTypeArguments.Length > 0 && o.GetGenericTypeDefinition() == typeof(ICrudService<,>)); if (crudInterface == null) { @@ -54,7 +58,9 @@ public static WebApplication MapAllCrud(this WebApplication app, Assembly assemb var entityType = crudInterface.GenericTypeArguments[0]; var requestType = crudInterface.GenericTypeArguments[1]; - + var crudOptions = allCrudOptions.GetOptions().ContainsKey(entityType) + ? allCrudOptions.GetOptions()[entityType] + : null; var method = extensionType.GetMethod(nameof(MapCrud)) ?.MakeGenericMethod(entityType, requestType, serviceType); method?.Invoke(null, new object?[] @@ -65,39 +71,62 @@ public static WebApplication MapAllCrud(this WebApplication app, Assembly assemb if (crudEndpoints.ReferenceType != null) opt.WithReference(crudEndpoints.ReferenceType); + + crudOptions?.Invoke(opt); }) }); } return app; } - + /// - /// Map CRUD endpoints + /// Map Readers endpoints /// /// The web application /// The options /// - /// /// /// - public static WebApplication MapCrud(this WebApplication app, + public static WebApplication MapReaders(this WebApplication app, Action>? options = null) where TEntity : class, new() - where TService : ICrudServiceBase + where TService : IReaderService { var crudOptions = new CrudOptions(); options?.Invoke(crudOptions); if ((crudOptions.Verbs & CrudEndpointVerbs.OnlyGetById) == CrudEndpointVerbs.OnlyGetById) { - app.MapGetById(crudOptions); + app.MapGetById(crudOptions); } if ((crudOptions.Verbs & CrudEndpointVerbs.OnlyGetPaged) == CrudEndpointVerbs.OnlyGetPaged) { - app.MapGetPagedList(crudOptions); + app.MapGetPagedList(crudOptions); } + + return app; + } + + /// + /// Map CRUD endpoints + /// + /// The web application + /// The options + /// + /// + /// + /// + public static WebApplication MapCrud(this WebApplication app, + Action>? options = null) + where TEntity : class, new() + where TService : ICrudService + { + var crudOptions = new CrudOptions(); + options?.Invoke(crudOptions); + + app.MapReaders(options); if ((crudOptions.Verbs & CrudEndpointVerbs.OnlyCreate) == CrudEndpointVerbs.OnlyCreate) { @@ -132,13 +161,13 @@ private static string GetPattern(bool withId = false, Type? referenceTy return pattern; } - private static WebApplication MapGetById(this WebApplication app, + private static WebApplication MapGetById(this WebApplication app, CrudOptions options) where TEntity : class, new() - where TService : ICrudServiceBase + where TService : IReaderService { var pattern = GetPattern(true, options.Get.ReferenceType); - var handlers = new CrudEndpointHandlers(options); + var handlers = new ReaderEndpointHandlers(options); Delegate handler = options.Get.ReferenceType != null ? handlers.GetReferencedById : handlers.GetById; @@ -151,13 +180,13 @@ private static WebApplication MapGetById(this WebAp return app; } - private static WebApplication MapGetPagedList(this WebApplication app, + private static WebApplication MapGetPagedList(this WebApplication app, CrudOptions options) where TEntity : class, new() - where TService : ICrudServiceBase + where TService : IReaderService { var pattern = GetPattern(false, options.List.ReferenceType); - var handlers = new CrudEndpointHandlers(options); + var handlers = new ReaderEndpointHandlers(options); Delegate handler = options.List.ReferenceType != null ? handlers.GetReferencedPagedList : handlers.GetPagedList; @@ -172,7 +201,7 @@ private static WebApplication MapGetPagedList(this private static WebApplication MapCreate(this WebApplication app, CrudOptions options) where TEntity : class, new() - where TService : ICrudServiceBase + where TService : ICrudService { var pattern = GetPattern(false, options.Create.ReferenceType); var handlers = new CrudEndpointHandlers(options); @@ -189,7 +218,7 @@ private static WebApplication MapCreate(this WebApp private static WebApplication MapUpdate(this WebApplication app, CrudOptions options) where TEntity : class, new() - where TService : ICrudServiceBase + where TService : ICrudService { var pattern = GetPattern(true, options.Update.ReferenceType); var handlers = new CrudEndpointHandlers(options); @@ -207,7 +236,7 @@ private static WebApplication MapUpdate(this WebApp private static WebApplication MapDelete(this WebApplication app, CrudOptions options) where TEntity : class, new() - where TService : ICrudServiceBase + where TService : ICrudService { var pattern = GetPattern(true, options.Delete.ReferenceType); var handlers = new CrudEndpointHandlers(options); diff --git a/src/eQuantic.Core.Api.Crud/Handlers/CrudEndpointHandlers.cs b/src/eQuantic.Core.Api.Crud/Handlers/CrudEndpointHandlers.cs index b1bce39..5352d81 100644 --- a/src/eQuantic.Core.Api.Crud/Handlers/CrudEndpointHandlers.cs +++ b/src/eQuantic.Core.Api.Crud/Handlers/CrudEndpointHandlers.cs @@ -18,7 +18,7 @@ namespace eQuantic.Core.Api.Crud.Handlers; /// public sealed class CrudEndpointHandlers where TEntity : class, new() - where TService : ICrudServiceBase + where TService : ICrudService { private readonly CrudOptions _options; @@ -30,78 +30,6 @@ public CrudEndpointHandlers(CrudOptions options) { _options = options; } - - /// - /// Get referenced entity by identifier - /// - /// - /// - /// - /// - public async Task, NotFound>> GetReferencedById( - [FromRoute] int referenceId, - [FromRoute] int id, - [FromServices]TService service) - { - var request = new ItemRequest(referenceId, id); - return await GetById(request, service); - } - - /// - /// Get entity by identifier - /// - /// - /// - /// - public async Task, NotFound>> GetById( - [FromRoute] int id, - [FromServices]TService service) - { - var request = new ItemRequest(id); - return await GetById(request, service); - } - - /// - /// Get paged list of referenced entity by criteria - /// - /// - /// - /// - /// - /// - /// - /// - public async Task>> GetReferencedPagedList( - [FromRoute] int referenceId, - [FromQuery] int? pageIndex, - [FromQuery] int? pageSize, - [FromQuery] IFiltering[]? filterBy, - [FromQuery] ISorting[]? orderBy, - [FromServices]TService service) - { - var request = new PagedListRequest(referenceId, pageIndex, pageSize, filterBy, orderBy); - return await GetPagedList(request, service); - } - - /// - /// Get paged list of entity by criteria - /// - /// - /// - /// - /// - /// - /// - public async Task>> GetPagedList( - [FromQuery] int? pageIndex, - [FromQuery] int? pageSize, - [FromQuery] IFiltering[]? filterBy, - [FromQuery] ISorting[]? orderBy, - [FromServices]TService service) - { - var request = new PagedListRequest(pageIndex, pageSize, filterBy, orderBy); - return await GetPagedList(request, service); - } /// /// Create an entity @@ -197,23 +125,6 @@ public async Task> ReferencedDelete( return await Delete(request, service); } - private static async Task, NotFound>> GetById(ItemRequest request, TService service) - { - var result = await service.GetByIdAsync(request); - if (result != null) - return TypedResults.Ok(result); - - return TypedResults.NotFound(); - } - - private static async Task>> GetPagedList( - PagedListRequest request, - TService service) - { - var result = await service.GetPagedListAsync(request); - return TypedResults.Ok(new PagedListResult(result)); - } - private async Task> Create( CreateRequest request, TService service) diff --git a/src/eQuantic.Core.Api.Crud/Handlers/ReaderEndpointHandlers.cs b/src/eQuantic.Core.Api.Crud/Handlers/ReaderEndpointHandlers.cs new file mode 100644 index 0000000..8fa3a40 --- /dev/null +++ b/src/eQuantic.Core.Api.Crud/Handlers/ReaderEndpointHandlers.cs @@ -0,0 +1,117 @@ +using eQuantic.Core.Api.Crud.Options; +using eQuantic.Core.Application.Crud.Entities.Requests; +using eQuantic.Core.Application.Crud.Services; +using eQuantic.Core.Application.Entities.Results; +using eQuantic.Linq.Filter; +using eQuantic.Linq.Sorter; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.HttpResults; +using Microsoft.AspNetCore.Mvc; + +namespace eQuantic.Core.Api.Crud.Handlers; + +public class ReaderEndpointHandlers + where TEntity : class, new() + where TService : IReaderService +{ + private readonly CrudOptions _options; + + /// + /// Initializes a new instance of the class + /// + /// + public ReaderEndpointHandlers(CrudOptions options) + { + _options = options; + } + + /// + /// Get referenced entity by identifier + /// + /// + /// + /// + /// + public async Task, NotFound>> GetReferencedById( + [FromRoute] int referenceId, + [FromRoute] int id, + [FromServices]TService service) + { + var request = new ItemRequest(referenceId, id); + return await GetById(request, service); + } + + /// + /// Get entity by identifier + /// + /// + /// + /// + public async Task, NotFound>> GetById( + [FromRoute] int id, + [FromServices]TService service) + { + var request = new ItemRequest(id); + return await GetById(request, service); + } + + /// + /// Get paged list of referenced entity by criteria + /// + /// + /// + /// + /// + /// + /// + /// + public async Task>> GetReferencedPagedList( + [FromRoute] int referenceId, + [FromQuery] int? pageIndex, + [FromQuery] int? pageSize, + [FromQuery] IFiltering[]? filterBy, + [FromQuery] ISorting[]? orderBy, + [FromServices]TService service) + { + var request = new PagedListRequest(referenceId, pageIndex, pageSize, filterBy, orderBy); + return await GetPagedList(request, service); + } + + /// + /// Get paged list of entity by criteria + /// + /// + /// + /// + /// + /// + /// + public async Task>> GetPagedList( + [FromQuery] int? pageIndex, + [FromQuery] int? pageSize, + [FromQuery] IFiltering[]? filterBy, + [FromQuery] ISorting[]? orderBy, + [FromServices]TService service) + { + var request = new PagedListRequest(pageIndex, pageSize, filterBy, orderBy); + return await GetPagedList(request, service); + } + + private static async Task, NotFound>> GetById(ItemRequest request, TService service) + { + var result = await service.GetByIdAsync(request); + if (result != null) + return TypedResults.Ok(result); + + return TypedResults.NotFound(); + } + + private static async Task>> GetPagedList( + PagedListRequest request, + TService service) + { + var result = await service.GetPagedListAsync(request); + return TypedResults.Ok(new PagedListResult(result)); + } + +} \ No newline at end of file diff --git a/src/eQuantic.Core.Api.Crud/Options/AllCrudOptions.cs b/src/eQuantic.Core.Api.Crud/Options/AllCrudOptions.cs new file mode 100644 index 0000000..ed7203b --- /dev/null +++ b/src/eQuantic.Core.Api.Crud/Options/AllCrudOptions.cs @@ -0,0 +1,39 @@ +using System.Reflection; + +namespace eQuantic.Core.Api.Crud.Options; + +public class AllCrudOptions +{ + private Assembly? _assembly = null; + private readonly Dictionary> _options = new(); + + public AllCrudOptions FromAssembly(Assembly assembly) + { + _assembly = assembly; + return this; + } + + public EntityCrudOptions For() + { + return new EntityCrudOptions(this); + } + + internal Assembly? GetAssembly() => _assembly; + internal Dictionary> GetOptions() => _options; + + public class EntityCrudOptions + { + private readonly AllCrudOptions _allCrudOptions; + + public EntityCrudOptions(AllCrudOptions allCrudOptions) + { + _allCrudOptions = allCrudOptions; + } + + public AllCrudOptions UseOptions(Action options) + { + _allCrudOptions._options.Add(typeof(TEntity), options); + return _allCrudOptions; + } + } +} \ No newline at end of file diff --git a/src/eQuantic.Core.Api.Crud/eQuantic.Core.Api.Crud.csproj b/src/eQuantic.Core.Api.Crud/eQuantic.Core.Api.Crud.csproj index 1a95bcd..8d3f865 100644 --- a/src/eQuantic.Core.Api.Crud/eQuantic.Core.Api.Crud.csproj +++ b/src/eQuantic.Core.Api.Crud/eQuantic.Core.Api.Crud.csproj @@ -6,9 +6,9 @@ eQuantic Systems eQuantic.Core.Api.Crud eQuantic.Core.Api.Crud - 1.0.8.0 - 1.0.8.0 - 1.0.8.0 + 1.0.9.0 + 1.0.9.0 + 1.0.9.0 net7.0 eQuantic.Core.Api.Crud @@ -45,7 +45,7 @@ - + diff --git a/src/eQuantic.Core.Api.Sample/Program.cs b/src/eQuantic.Core.Api.Sample/Program.cs index ce53f7f..78f7274 100644 --- a/src/eQuantic.Core.Api.Sample/Program.cs +++ b/src/eQuantic.Core.Api.Sample/Program.cs @@ -45,6 +45,6 @@ app.UseHttpsRedirection(); app.UseRouting(); app.MapControllers(); -app.MapAllCrud(assembly); +app.MapAllCrud(opt => opt.FromAssembly(assembly)); app.Run(); \ No newline at end of file diff --git a/src/eQuantic.Core.Api.Sample/Services/ChildExampleService.cs b/src/eQuantic.Core.Api.Sample/Services/ChildExampleService.cs index 13d4163..3dc5260 100644 --- a/src/eQuantic.Core.Api.Sample/Services/ChildExampleService.cs +++ b/src/eQuantic.Core.Api.Sample/Services/ChildExampleService.cs @@ -6,7 +6,7 @@ namespace eQuantic.Core.Api.Sample.Services; -public interface IChildExampleService : ICrudServiceBase {} +public interface IChildExampleService : ICrudService {} [MapCrudEndpoints(ReferenceType = typeof(Example))] public class ChildExampleService : CrudServiceBase, IChildExampleService diff --git a/src/eQuantic.Core.Api.Sample/Services/ExampleService.cs b/src/eQuantic.Core.Api.Sample/Services/ExampleService.cs index 64c1658..9b84e6a 100644 --- a/src/eQuantic.Core.Api.Sample/Services/ExampleService.cs +++ b/src/eQuantic.Core.Api.Sample/Services/ExampleService.cs @@ -6,7 +6,7 @@ namespace eQuantic.Core.Api.Sample.Services; -public interface IExampleService : ICrudServiceBase {} +public interface IExampleService : ICrudService {} [MapCrudEndpoints] public class ExampleService : CrudServiceBase, IExampleService diff --git a/src/eQuantic.Core.Api.Sample/eQuantic.Core.Api.Sample.csproj b/src/eQuantic.Core.Api.Sample/eQuantic.Core.Api.Sample.csproj index bf228fa..a5abb5c 100644 --- a/src/eQuantic.Core.Api.Sample/eQuantic.Core.Api.Sample.csproj +++ b/src/eQuantic.Core.Api.Sample/eQuantic.Core.Api.Sample.csproj @@ -12,7 +12,7 @@ - + all diff --git a/src/eQuantic.Core.Api/eQuantic.Core.Api.csproj b/src/eQuantic.Core.Api/eQuantic.Core.Api.csproj index 66a6d52..f3f1777 100644 --- a/src/eQuantic.Core.Api/eQuantic.Core.Api.csproj +++ b/src/eQuantic.Core.Api/eQuantic.Core.Api.csproj @@ -6,9 +6,9 @@ eQuantic Systems eQuantic.Core.Api eQuantic.Core.Api - 1.0.8.0 - 1.0.8.0 - 1.0.8.0 + 1.0.9.0 + 1.0.9.0 + 1.0.9.0 net7.0 eQuantic.Core.Api diff --git a/src/eQuantic.Core.Application.Crud/Services/CrudServiceBase.cs b/src/eQuantic.Core.Application.Crud/Services/CrudServiceBase.cs index acf652e..1e28aee 100644 --- a/src/eQuantic.Core.Application.Crud/Services/CrudServiceBase.cs +++ b/src/eQuantic.Core.Application.Crud/Services/CrudServiceBase.cs @@ -1,91 +1,19 @@ using eQuantic.Core.Application.Crud.Entities.Requests; using eQuantic.Core.Application.Entities.Data; using eQuantic.Core.Application.Exceptions; -using eQuantic.Core.Collections; using eQuantic.Core.Data.Repository; -using eQuantic.Core.Data.Repository.Sql; using eQuantic.Linq.Filter; -using eQuantic.Linq.Filter.Extensions; -using eQuantic.Linq.Sorter.Extensions; using eQuantic.Linq.Specification; using eQuantic.Mapper; namespace eQuantic.Core.Application.Crud.Services; -public abstract class CrudServiceBase : ICrudServiceBase +public abstract class CrudServiceBase : ReaderServiceBase, ICrudService where TEntity : class, new() where TDataEntity : EntityDataBase, new() { - protected IDefaultUnitOfWork UnitOfWork { get; } - protected IMapperFactory MapperFactory { get; } - protected IAsyncQueryableRepository Repository { get; } - - protected CrudServiceBase(IDefaultUnitOfWork unitOfWork, IMapperFactory mapperFactory) - { - UnitOfWork = unitOfWork; - MapperFactory = mapperFactory; - Repository = unitOfWork.GetAsyncQueryableRepository(); - } - - public async Task GetByIdAsync(ItemRequest request, CancellationToken cancellationToken = default) - { - var item = await Repository.GetAsync(request.Id, opt => - { - opt.WithProperties(OnGetProperties()); - }, cancellationToken); - - if (item == null) - { - throw new EntityNotFoundException(request.Id); - } - - if (request is IReferencedRequest referencedRequest && item is IWithReferenceId referencedItem) - { - if (referencedItem.GetReferenceId() != referencedRequest.ReferenceId) - { - throw new InvalidReferenceException(referencedRequest.ReferenceId); - } - } - - var result = OnMapEntity(item); - - await OnBeforeGetByIdAsync(item, result, cancellationToken); - return result; - } - - public async Task?> GetPagedListAsync(PagedListRequest request, - CancellationToken cancellationToken = default) + protected CrudServiceBase(IDefaultUnitOfWork unitOfWork, IMapperFactory mapperFactory) : base(unitOfWork, mapperFactory) { - var filtering = request.Filtering - .Cast(opt => opt.ExcludeUnmapped()).ToList(); - - SetReferenceFiltering(request, filtering); - - var sorting = request.Sorting - .Cast(opt => opt.ExcludeUnmapped()); - - Specification specification = filtering.Any() - ? new EntityFilterSpecification(filtering.ToArray()) - : new TrueSpecification(); - - var count = await Repository.CountAsync(specification, cancellationToken); - var pagedList = (await Repository.GetPagedAsync(specification, request.PageIndex, request.PageSize, - config => - { - config - .WithSorting(sorting) - .WithProperties(OnGetProperties()); - }, cancellationToken)) - .ToList(); - - var list = pagedList - .Select(dataEntity => OnMapEntity(dataEntity)!) - .Where(item => item != null) - .ToList(); - - await OnBeforeGetPagedListAsync(pagedList, list, cancellationToken); - - return new PagedList(list, count) { PageIndex = request.PageIndex, PageSize = request.PageSize }; } public async Task CreateAsync(CreateRequest request, CancellationToken cancellationToken = default) @@ -109,7 +37,7 @@ public async Task CreateAsync(CreateRequest request, Cancellation } await Repository.AddAsync(item); - await Repository.UnitOfWork.CommitAsync(); + await Repository.UnitOfWork.CommitAsync(cancellationToken); await OnBeforeCreateAsync(item, cancellationToken); return item.Id; @@ -133,7 +61,7 @@ public async Task UpdateAsync(UpdateRequest request, Cancellatio } await Repository.ModifyAsync(item); - await Repository.UnitOfWork.CommitAsync(); + await Repository.UnitOfWork.CommitAsync(cancellationToken); await OnBeforeUpdateAsync(item, cancellationToken); return true; @@ -157,40 +85,18 @@ public async Task DeleteAsync(ItemRequest request, CancellationToken cance await Repository.RemoveAsync(item); } - await Repository.UnitOfWork.CommitAsync(); + await Repository.UnitOfWork.CommitAsync(cancellationToken); await OnBeforeDeleteAsync(item, cancellationToken); return true; } - protected virtual string[] OnGetProperties() - { - return Array.Empty(); - } - - protected virtual TEntity? OnMapEntity(TDataEntity dataEntity) - { - var mapper = MapperFactory.GetMapper(); - return mapper?.Map(dataEntity); - } - protected virtual TDataEntity? OnMapRequest(TRequest? request, TDataEntity? dataEntity = null) { var mapper = MapperFactory.GetMapper(); return mapper?.Map(request, dataEntity); } - - protected virtual Task OnBeforeGetByIdAsync(TDataEntity? dataEntity, TEntity? entity, CancellationToken cancellationToken = default) - { - return Task.CompletedTask; - } - protected virtual Task OnBeforeGetPagedListAsync(IEnumerable dataEntityList, - IEnumerable entityList, CancellationToken cancellationToken = default) - { - return Task.CompletedTask; - } - protected virtual Task OnAfterCreateAsync(TDataEntity? dataEntity, CancellationToken cancellationToken = default) { return Task.CompletedTask; @@ -248,18 +154,4 @@ protected virtual Task OnBeforeDeleteAsync(TDataEntity? dataEntity, Cancellation return filterByRef; } - - private static void SetReferenceFiltering(TAnyRequest request, - ICollection> filtering) - { - var filterByRef = GetReferenceFiltering(request); - - if (filterByRef == null) - return; - - if (filtering.All(f => f.ColumnName != filterByRef.ColumnName)) - { - filtering.Add(filterByRef); - } - } } \ No newline at end of file diff --git a/src/eQuantic.Core.Application.Crud/Services/ICrudServiceBase.cs b/src/eQuantic.Core.Application.Crud/Services/ICrudService.cs similarity index 72% rename from src/eQuantic.Core.Application.Crud/Services/ICrudServiceBase.cs rename to src/eQuantic.Core.Application.Crud/Services/ICrudService.cs index 881d780..6eafccf 100644 --- a/src/eQuantic.Core.Application.Crud/Services/ICrudServiceBase.cs +++ b/src/eQuantic.Core.Application.Crud/Services/ICrudService.cs @@ -4,15 +4,25 @@ namespace eQuantic.Core.Application.Crud.Services; -public interface ICrudServiceBase : IApplicationService +public interface IReaderService : IApplicationService { + } -public interface ICrudServiceBase : ICrudServiceBase +public interface IReaderService : IReaderService where TEntity : class, new() { Task GetByIdAsync(ItemRequest request, CancellationToken cancellationToken = default); Task?> GetPagedListAsync(PagedListRequest request, CancellationToken cancellationToken = default); +} + +public interface ICrudService : IReaderService +{ +} + +public interface ICrudService : ICrudService, IReaderService + where TEntity : class, new() +{ Task CreateAsync(CreateRequest request, CancellationToken cancellationToken = default); Task UpdateAsync(UpdateRequest request, CancellationToken cancellationToken = default); Task DeleteAsync(ItemRequest request, CancellationToken cancellationToken = default); diff --git a/src/eQuantic.Core.Application.Crud/Services/ReaderServiceBase.cs b/src/eQuantic.Core.Application.Crud/Services/ReaderServiceBase.cs new file mode 100644 index 0000000..a88ba9b --- /dev/null +++ b/src/eQuantic.Core.Application.Crud/Services/ReaderServiceBase.cs @@ -0,0 +1,141 @@ +using eQuantic.Core.Application.Crud.Entities.Requests; +using eQuantic.Core.Application.Entities.Data; +using eQuantic.Core.Application.Exceptions; +using eQuantic.Core.Collections; +using eQuantic.Core.Data.Repository; +using eQuantic.Core.Data.Repository.Sql; +using eQuantic.Linq.Filter; +using eQuantic.Linq.Filter.Extensions; +using eQuantic.Linq.Sorter.Extensions; +using eQuantic.Linq.Specification; +using eQuantic.Mapper; + +namespace eQuantic.Core.Application.Crud.Services; + +public abstract class ReaderServiceBase : IReaderService + where TEntity : class, new() + where TDataEntity : EntityDataBase, new() +{ + protected IDefaultUnitOfWork UnitOfWork { get; } + protected IMapperFactory MapperFactory { get; } + protected IAsyncQueryableRepository Repository { get; } + + protected ReaderServiceBase(IDefaultUnitOfWork unitOfWork, IMapperFactory mapperFactory) + { + UnitOfWork = unitOfWork; + MapperFactory = mapperFactory; + Repository = unitOfWork.GetAsyncQueryableRepository(); + } + + public async Task GetByIdAsync(ItemRequest request, CancellationToken cancellationToken = default) + { + var item = await Repository.GetAsync(request.Id, opt => + { + opt.WithProperties(OnGetProperties()); + }, cancellationToken); + + if (item == null) + { + throw new EntityNotFoundException(request.Id); + } + + if (request is IReferencedRequest referencedRequest && item is IWithReferenceId referencedItem) + { + if (referencedItem.GetReferenceId() != referencedRequest.ReferenceId) + { + throw new InvalidReferenceException(referencedRequest.ReferenceId); + } + } + + var result = OnMapEntity(item); + + await OnBeforeGetByIdAsync(item, result, cancellationToken); + return result; + } + + public async Task?> GetPagedListAsync(PagedListRequest request, + CancellationToken cancellationToken = default) + { + var filtering = request.Filtering + .Cast(opt => opt.ExcludeUnmapped()).ToList(); + + SetReferenceFiltering(request, filtering); + + var sorting = request.Sorting + .Cast(opt => opt.ExcludeUnmapped()); + + Specification specification = filtering.Any() + ? new EntityFilterSpecification(filtering.ToArray()) + : new TrueSpecification(); + + var count = await Repository.CountAsync(specification, cancellationToken); + var pagedList = (await Repository.GetPagedAsync(specification, request.PageIndex, request.PageSize, + config => + { + config + .WithSorting(sorting) + .WithProperties(OnGetProperties()); + }, cancellationToken)) + .ToList(); + + var list = pagedList + .Select(dataEntity => OnMapEntity(dataEntity)!) + .Where(item => item != null) + .ToList(); + + await OnBeforeGetPagedListAsync(pagedList, list, cancellationToken); + + return new PagedList(list, count) { PageIndex = request.PageIndex, PageSize = request.PageSize }; + } + + protected virtual string[] OnGetProperties() + { + return Array.Empty(); + } + + protected virtual TEntity? OnMapEntity(TDataEntity dataEntity) + { + var mapper = MapperFactory.GetMapper(); + return mapper?.Map(dataEntity); + } + + protected virtual Task OnBeforeGetByIdAsync(TDataEntity? dataEntity, TEntity? entity, CancellationToken cancellationToken = default) + { + return Task.CompletedTask; + } + + protected virtual Task OnBeforeGetPagedListAsync(IEnumerable dataEntityList, + IEnumerable entityList, CancellationToken cancellationToken = default) + { + return Task.CompletedTask; + } + + private static IFiltering? GetReferenceFiltering(TAnyRequest request) + { + if (request is not IReferencedRequest referencedRequest) + return null; + + var dataEntity = new TDataEntity(); + if (dataEntity is not IWithReferenceId referencedDataEntity) + return null; + + referencedDataEntity.SetReferenceId(referencedRequest.ReferenceId); + var filterByRef = referencedDataEntity.GetReferenceFiltering(); + + return filterByRef; + } + + private static void SetReferenceFiltering(TAnyRequest request, + ICollection> filtering) + { + var filterByRef = GetReferenceFiltering(request); + + if (filterByRef == null) + return; + + if (filtering.All(f => f.ColumnName != filterByRef.ColumnName)) + { + filtering.Add(filterByRef); + } + } +} \ No newline at end of file diff --git a/src/eQuantic.Core.Application.Crud/eQuantic.Core.Application.Crud.csproj b/src/eQuantic.Core.Application.Crud/eQuantic.Core.Application.Crud.csproj index 98c052c..b5eda3f 100644 --- a/src/eQuantic.Core.Application.Crud/eQuantic.Core.Application.Crud.csproj +++ b/src/eQuantic.Core.Application.Crud/eQuantic.Core.Application.Crud.csproj @@ -6,9 +6,9 @@ eQuantic Systems eQuantic.Core.Application.Crud eQuantic.Core.Application.Crud - 1.0.8.0 - 1.0.8.0 - 1.0.8.0 + 1.0.9.0 + 1.0.9.0 + 1.0.9.0 netstandard2.1;net7.0 eQuantic.Core.Application.Crud @@ -37,7 +37,7 @@ - + @@ -52,6 +52,6 @@ - + diff --git a/src/eQuantic.Core.Application/Entities/Results/ErrorResult.cs b/src/eQuantic.Core.Application/Entities/Results/ErrorResult.cs index 14a8a3c..534fa5d 100644 --- a/src/eQuantic.Core.Application/Entities/Results/ErrorResult.cs +++ b/src/eQuantic.Core.Application/Entities/Results/ErrorResult.cs @@ -2,7 +2,7 @@ namespace eQuantic.Core.Application.Entities.Results; public class ErrorResult { - public string Message { get; set; } + public string? Message { get; set; } public string? Details { get; set; } public ErrorResult() diff --git a/src/eQuantic.Core.Application/eQuantic.Core.Application.csproj b/src/eQuantic.Core.Application/eQuantic.Core.Application.csproj index 6d5e13a..0943bf5 100644 --- a/src/eQuantic.Core.Application/eQuantic.Core.Application.csproj +++ b/src/eQuantic.Core.Application/eQuantic.Core.Application.csproj @@ -6,9 +6,9 @@ eQuantic Systems eQuantic.Core.Application eQuantic.Core.Application - 1.0.8.0 - 1.0.8.0 - 1.0.8.0 + 1.0.9.0 + 1.0.9.0 + 1.0.9.0 netstandard2.1;net7.0 eQuantic.Core.Application @@ -36,7 +36,7 @@ - +