diff --git a/DigitalData.Core.API/BasicCRUDControllerBase.cs b/DigitalData.Core.API/BasicCRUDControllerBase.cs index 16e071e..871b437 100644 --- a/DigitalData.Core.API/BasicCRUDControllerBase.cs +++ b/DigitalData.Core.API/BasicCRUDControllerBase.cs @@ -1,4 +1,3 @@ -using DigitalData.Core.Abstractions; using DigitalData.Core.Application.Interfaces; using Microsoft.AspNetCore.Mvc; @@ -6,10 +5,11 @@ namespace DigitalData.Core.API { [ApiController] [Route("api/[controller]")] + [Obsolete("Use MediatR")] public class BasicCRUDControllerBase : CRUDControllerBase where TCRUDService : ICRUDService - where TDto : class, IUnique - where TEntity : class, IUnique + where TDto : class + where TEntity : class { public BasicCRUDControllerBase(ILogger logger, TCRUDService service) : base(logger, service) { diff --git a/DigitalData.Core.API/CRUDControllerBase.cs b/DigitalData.Core.API/CRUDControllerBase.cs index de61856..847e64c 100644 --- a/DigitalData.Core.API/CRUDControllerBase.cs +++ b/DigitalData.Core.API/CRUDControllerBase.cs @@ -1,4 +1,3 @@ -using DigitalData.Core.Abstractions; using DigitalData.Core.Application.Interfaces; using Microsoft.AspNetCore.Mvc; using DigitalData.Core.Application.DTO; @@ -15,12 +14,13 @@ namespace DigitalData.Core.API /// The type of the entity's identifier. [ApiController] [Route("api/[controller]")] + [Obsolete("Use MediatR")] public class CRUDControllerBase : ControllerBase where TCRUDService : ICRUDService where TCreateDto : class where TReadDto : class - where TUpdateDto : class, IUnique - where TEntity : class, IUnique + where TUpdateDto : class + where TEntity : class { protected readonly ILogger _logger; protected readonly TCRUDService _service; diff --git a/DigitalData.Core.API/CRUDControllerBaseWithErrorHandling.cs b/DigitalData.Core.API/CRUDControllerBaseWithErrorHandling.cs index 1692c95..26fe7e3 100644 --- a/DigitalData.Core.API/CRUDControllerBaseWithErrorHandling.cs +++ b/DigitalData.Core.API/CRUDControllerBaseWithErrorHandling.cs @@ -1,4 +1,3 @@ -using DigitalData.Core.Abstractions; using DigitalData.Core.Application.Interfaces; using Microsoft.AspNetCore.Mvc; using DigitalData.Core.Application.DTO; @@ -16,12 +15,13 @@ namespace DigitalData.Core.API /// The type of the entity's identifier. [ApiController] [Route("api/[controller]")] + [Obsolete("Use MediatR")] public class CRUDControllerBaseWithErrorHandling : ControllerBase where TCRUDService : ICRUDService where TCreateDto : class where TReadDto : class - where TUpdateDto : class, IUnique - where TEntity : class, IUnique + where TUpdateDto : class + where TEntity : class { protected readonly ILogger _logger; protected readonly TCRUDService _service; diff --git a/DigitalData.Core.API/CSPMiddleware.cs b/DigitalData.Core.API/CSPMiddleware.cs index 63ab642..4e1f2ab 100644 --- a/DigitalData.Core.API/CSPMiddleware.cs +++ b/DigitalData.Core.API/CSPMiddleware.cs @@ -1,47 +1,46 @@ -namespace DigitalData.Core.API +namespace DigitalData.Core.API; + +/// +/// Middleware to add Content Security Policy (CSP) headers to the HTTP response. +/// +public class CSPMiddleware { + private readonly RequestDelegate _next; + private readonly string _policy; + /// - /// Middleware to add Content Security Policy (CSP) headers to the HTTP response. + /// Initializes a new instance of the class. /// - public class CSPMiddleware + /// The next middleware in the request pipeline. + /// The CSP policy string with placeholders for nonces. + public CSPMiddleware(RequestDelegate next, string policy) { - private readonly RequestDelegate _next; - private readonly string _policy; + _next = next; + _policy = policy; + } - /// - /// Initializes a new instance of the class. - /// - /// The next middleware in the request pipeline. - /// The CSP policy string with placeholders for nonces. - public CSPMiddleware(RequestDelegate next, string policy) + /// + /// Invokes the middleware to add the CSP header to the response. + /// + /// The HTTP context. + /// A task that represents the completion of request processing. + public async Task Invoke(HttpContext context) + { + // Generate a nonce (number used once) for inline scripts and styles + var nonce = Convert.ToBase64String(Guid.NewGuid().ToByteArray()); + + // Store the nonce in the context items for later use + context.Items["csp-nonce"] = nonce; + + // Add the CSP header to the response + context.Response.OnStarting(() => { - _next = next; - _policy = policy; - } + context.Response.Headers.Append("Content-Security-Policy", + string.Format(_policy, nonce)); + return Task.CompletedTask; + }); - /// - /// Invokes the middleware to add the CSP header to the response. - /// - /// The HTTP context. - /// A task that represents the completion of request processing. - public async Task Invoke(HttpContext context) - { - // Generate a nonce (number used once) for inline scripts and styles - var nonce = Convert.ToBase64String(Guid.NewGuid().ToByteArray()); - - // Store the nonce in the context items for later use - context.Items["csp-nonce"] = nonce; - - // Add the CSP header to the response - context.Response.OnStarting(() => - { - context.Response.Headers.Add("Content-Security-Policy", - string.Format(_policy, nonce)); - return Task.CompletedTask; - }); - - // Call the next middleware in the pipeline - await _next(context); - } + // Call the next middleware in the pipeline + await _next(context); } } \ No newline at end of file diff --git a/DigitalData.Core.API/ReadControllerBase.cs b/DigitalData.Core.API/ReadControllerBase.cs index 973e94a..61466f9 100644 --- a/DigitalData.Core.API/ReadControllerBase.cs +++ b/DigitalData.Core.API/ReadControllerBase.cs @@ -12,6 +12,7 @@ namespace DigitalData.Core.API /// The type of the entity's identifier. [ApiController] [Route("api/[controller]")] + [Obsolete("Use MediatR")] public class ReadControllerBase : ControllerBase where TReadService : IReadService where TReadDto : class diff --git a/DigitalData.Core.API/ReadControllerBaseWithErrorHandling.cs b/DigitalData.Core.API/ReadControllerBaseWithErrorHandling.cs index 928ddf9..774943e 100644 --- a/DigitalData.Core.API/ReadControllerBaseWithErrorHandling.cs +++ b/DigitalData.Core.API/ReadControllerBaseWithErrorHandling.cs @@ -13,6 +13,7 @@ namespace DigitalData.Core.API /// The type of the entity's identifier. [ApiController] [Route("api/[controller]")] + [Obsolete("Use MediatR")] public class ReadControllerBaseWithErrorHandling : ControllerBase where TReadService : IReadService where TReadDto : class diff --git a/DigitalData.Core.Abstractions/IUnique.cs b/DigitalData.Core.Abstractions/IUnique.cs deleted file mode 100644 index e92ea17..0000000 --- a/DigitalData.Core.Abstractions/IUnique.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace DigitalData.Core.Abstractions -{ - public interface IUnique - { - public T Id { get; } - } -} \ No newline at end of file diff --git a/DigitalData.Core.Application/BasicCRUDService.cs b/DigitalData.Core.Application/BasicCRUDService.cs index d781549..86217f8 100644 --- a/DigitalData.Core.Application/BasicCRUDService.cs +++ b/DigitalData.Core.Application/BasicCRUDService.cs @@ -1,5 +1,4 @@ using AutoMapper; -using DigitalData.Core.Abstractions; using DigitalData.Core.Application.Interfaces; using DigitalData.Core.Application.Interfaces.Repository; @@ -18,9 +17,10 @@ namespace DigitalData.Core.Application /// reducing the need for multiple DTOs and simplifying the data mapping process. It leverages AutoMapper for object mapping /// and a culture-specific translation service for any necessary text translations, ensuring a versatile and internationalized approach to CRUD operations. /// + [Obsolete("Use MediatR")] public class BasicCRUDService : CRUDService, IBasicCRUDService - where TCRUDRepository : ICRUDRepository where TDto : class, IUnique where TEntity : class, IUnique + where TCRUDRepository : ICRUDRepository where TDto : class where TEntity : class { /// /// Initializes a new instance of the BasicCRUDService with the specified repository, translation service, and AutoMapper configuration. diff --git a/DigitalData.Core.Application/CRUDService.cs b/DigitalData.Core.Application/CRUDService.cs index 5b2dd39..e235ccd 100644 --- a/DigitalData.Core.Application/CRUDService.cs +++ b/DigitalData.Core.Application/CRUDService.cs @@ -1,5 +1,4 @@ using AutoMapper; -using DigitalData.Core.Abstractions; using Microsoft.Extensions.Logging; using DigitalData.Core.Application.Interfaces.Repository; using DigitalData.Core.Application.Interfaces; @@ -14,8 +13,10 @@ namespace DigitalData.Core.Application /// The DTO type for read operations. /// The entity type. /// The type of the identifier for the entity. + /// + [Obsolete("Use MediatR")] public class CRUDService : ReadService, ICRUDService - where TCRUDRepository : ICRUDRepository where TCreateDto : class where TReadDto : class where TEntity : class, IUnique + where TCRUDRepository : ICRUDRepository where TCreateDto : class where TReadDto : class where TEntity : class { /// @@ -36,7 +37,7 @@ namespace DigitalData.Core.Application { var entity = _mapper.Map(createDto); var createdEntity = await _repository.CreateAsync(entity); - return createdEntity is null ? Result.Fail() : Result.Success(createdEntity.Id); + return createdEntity is null ? Result.Fail() : Result.Success(createdEntity.GetIdOrDefault()); } /// @@ -44,12 +45,12 @@ namespace DigitalData.Core.Application /// /// The DTO to update an entity from. /// A service message indicating success or failure. - public virtual async Task UpdateAsync(TUpdateDto updateDto) where TUpdateDto : IUnique + public virtual async Task UpdateAsync(TUpdateDto updateDto) { - var currentEntitiy = await _repository.ReadByIdAsync(updateDto.Id); + var currentEntitiy = await _repository.ReadByIdAsync(updateDto.GetIdOrDefault()); if (currentEntitiy is null) - return Result.Fail().Notice(LogLevel.Warning, Flag.NotFound, $"{updateDto.Id} is not found in update process of {GetType()} entity."); + return Result.Fail().Notice(LogLevel.Warning, Flag.NotFound, $"{updateDto.GetIdOrDefault()} is not found in update process of {GetType()} entity."); var entity = _mapper.Map(updateDto, currentEntitiy); diff --git a/DigitalData.Core.Application/DigitalData.Core.Application.csproj b/DigitalData.Core.Application/DigitalData.Core.Application.csproj index 04ace75..bee401f 100644 --- a/DigitalData.Core.Application/DigitalData.Core.Application.csproj +++ b/DigitalData.Core.Application/DigitalData.Core.Application.csproj @@ -27,7 +27,6 @@ - @@ -37,20 +36,22 @@ - - - + + + + + - - - + + + + + - - - - - - + + + + diff --git a/DigitalData.Core.Application/EntityExtensions.cs b/DigitalData.Core.Application/EntityExtensions.cs new file mode 100644 index 0000000..91b448c --- /dev/null +++ b/DigitalData.Core.Application/EntityExtensions.cs @@ -0,0 +1,94 @@ +namespace DigitalData.Core.Application; + +/// +/// Provides extension methods for retrieving the value of an 'Id' property from objects. +/// +public static class EntityExtensions +{ + /// + /// Attempts to retrieve the value of the 'Id' property from the specified object. + /// + /// The expected type of the 'Id' property. + /// The object from which to retrieve the 'Id' property. + /// + /// The value of the 'Id' property if it exists and is of type ; otherwise, default. + /// + public static TId? GetIdOrDefault(this object? obj) + { + var prop = obj?.GetType().GetProperty("Id"); + return prop is not null && prop.GetValue(obj) is TId id ? id : default; + } + + /// + /// Retrieves the value of the 'Id' property from the specified object, or throws an exception if not found or of the wrong type. + /// + /// The expected type of the 'Id' property. + /// The object from which to retrieve the 'Id' property. + /// The value of the 'Id' property. + /// + /// Thrown if the object does not have a readable 'Id' property of type . + /// + public static TId? GetId(this object? obj) + => obj.GetIdOrDefault() + ?? throw new InvalidOperationException($"The object of type '{obj?.GetType().FullName ?? "null"}' does not have a readable 'Id' property of type '{typeof(TId).FullName}'."); + + /// + /// Tries to retrieve the value of the 'Id' property from the specified object. + /// + /// The expected type of the 'Id' property. + /// The object from which to retrieve the 'Id' property. + /// When this method returns, contains the value of the 'Id' property if found; otherwise, the default value for the type. + /// + /// true if the 'Id' property was found and is of type ; otherwise, false. + /// + public static bool TryGetId(object? obj, out TId id) + { +#pragma warning disable CS8601 + id = obj.GetIdOrDefault(); +#pragma warning restore CS8601 + return id is not null; + } + + /// + /// Attempts to retrieve the value of the 'Id' property from the specified object. + /// + /// The object from which to retrieve the 'Id' property. + /// + /// The value of the 'Id' property if it exists; otherwise, null. + /// + public static object? GetIdOrDefault(this object? obj) + { + var prop = obj?.GetType().GetProperty("Id"); + return prop?.GetValue(obj); + } + + /// + /// Retrieves the value of the 'Id' property from the specified object, or throws an exception if not found. + /// + /// The object from which to retrieve the 'Id' property. + /// The value of the 'Id' property. + /// + /// Thrown if the object does not have a readable 'Id' property. + /// + public static object GetId(this object? obj) + => obj.GetIdOrDefault() + ?? throw new InvalidOperationException($"The object of type '{obj?.GetType().FullName ?? "null"}' does not have a readable 'Id' property."); + + /// + /// Tries to retrieve the value of the 'Id' property from the specified object. + /// + /// The object from which to retrieve the 'Id' property. + /// + /// When this method returns, contains the value of the 'Id' property if found; otherwise, null. + /// + /// + /// true if the 'Id' property was found; otherwise, false. + /// + public static bool TryGetId(object? obj, out object id) + { +#pragma warning disable CS8601 + id = obj.GetIdOrDefault(); +#pragma warning restore CS8601 + return id is not null; + } +} \ No newline at end of file diff --git a/DigitalData.Core.Application/Interfaces/IBasicCRUDService.cs b/DigitalData.Core.Application/Interfaces/IBasicCRUDService.cs index 93339e2..6b4a2b2 100644 --- a/DigitalData.Core.Application/Interfaces/IBasicCRUDService.cs +++ b/DigitalData.Core.Application/Interfaces/IBasicCRUDService.cs @@ -1,6 +1,4 @@ -using DigitalData.Core.Abstractions; - -namespace DigitalData.Core.Application.Interfaces +namespace DigitalData.Core.Application.Interfaces { /// /// Implements a simplified CRUD service interface that uses a single Data Transfer Object (DTO) type for all CRUD operations, @@ -15,8 +13,9 @@ namespace DigitalData.Core.Application.Interfaces /// This interface is useful for entities that do not require different DTOs for different operations, /// allowing for a more concise and maintainable codebase when implementing services for such entities. /// + [Obsolete("Use MediatR")] public interface IBasicCRUDService : ICRUDService - where TDto : class, IUnique where TEntity : class, IUnique + where TDto : class where TEntity : class { } } \ No newline at end of file diff --git a/DigitalData.Core.Application/Interfaces/ICRUDService.cs b/DigitalData.Core.Application/Interfaces/ICRUDService.cs index 36da7b6..82f3bc8 100644 --- a/DigitalData.Core.Application/Interfaces/ICRUDService.cs +++ b/DigitalData.Core.Application/Interfaces/ICRUDService.cs @@ -1,10 +1,10 @@ -using DigitalData.Core.Abstractions; -using DigitalData.Core.Application.DTO; +using DigitalData.Core.Application.DTO; namespace DigitalData.Core.Application.Interfaces { + [Obsolete("Use MediatR")] public interface ICRUDService : IReadService - where TCreateDto : class where TReadDto : class where TEntity : class, IUnique + where TCreateDto : class where TReadDto : class where TEntity : class { /// /// Asynchronously creates a new entity based on the provided and returns the identifier of the created entity wrapped in a . @@ -21,6 +21,6 @@ namespace DigitalData.Core.Application.Interfaces /// /// The updateDTO with updated values for the entity. /// An Result indicating the outcome of the update operation, with an appropriate message. - Task UpdateAsync(TUpdateDto updateDto) where TUpdateDto : IUnique; + Task UpdateAsync(TUpdateDto updateDto); } } \ No newline at end of file diff --git a/DigitalData.Core.Application/Interfaces/IReadService.cs b/DigitalData.Core.Application/Interfaces/IReadService.cs index fa1ee7a..b687904 100644 --- a/DigitalData.Core.Application/Interfaces/IReadService.cs +++ b/DigitalData.Core.Application/Interfaces/IReadService.cs @@ -2,6 +2,7 @@ namespace DigitalData.Core.Application.Interfaces { + [Obsolete("Use MediatR")] public interface IReadService where TReadDto : class where TEntity : class { diff --git a/DigitalData.Core.Application/Interfaces/Repository/ICRUDRepository.cs b/DigitalData.Core.Application/Interfaces/Repository/ICRUDRepository.cs index af16ad3..27103f6 100644 --- a/DigitalData.Core.Application/Interfaces/Repository/ICRUDRepository.cs +++ b/DigitalData.Core.Application/Interfaces/Repository/ICRUDRepository.cs @@ -1,13 +1,11 @@ -using DigitalData.Core.Abstractions; - -namespace DigitalData.Core.Application.Interfaces.Repository +namespace DigitalData.Core.Application.Interfaces.Repository { /// /// Defines the contract for CRUD operations on a repository for entities of type TEntity. /// /// The type of the entity this repository works with. /// The type of the identifier for the entity. - public interface ICRUDRepository where TEntity : class, IUnique + public interface ICRUDRepository where TEntity : class { /// /// Adds a new entity to the repository. diff --git a/DigitalData.Core.Application/JWTService.cs b/DigitalData.Core.Application/JWTService.cs index f55505d..b13d5bd 100644 --- a/DigitalData.Core.Application/JWTService.cs +++ b/DigitalData.Core.Application/JWTService.cs @@ -3,62 +3,61 @@ using Microsoft.IdentityModel.Tokens; using System.IdentityModel.Tokens.Jwt; using System.Security.Cryptography; -namespace DigitalData.Core.Application +namespace DigitalData.Core.Application; + +/// +/// Implements the interface to manage JWT operations for claims of type . +/// +public class JWTService : IJWTService { + private readonly Func _factory; + /// - /// Implements the interface to manage JWT operations for claims of type . + /// Initializes a new instance of the class. /// - public class JWTService : IJWTService + /// A factory function to produce based on the claim value. + public JWTService(Func tokenDescriptorFactory) { - private readonly Func _factory; + _factory = tokenDescriptorFactory; + } - /// - /// Initializes a new instance of the class. - /// - /// A factory function to produce based on the claim value. - public JWTService(Func tokenDescriptorFactory) - { - _factory = tokenDescriptorFactory; - } + /// + /// Generates a symmetric security key with the specified byte size. + /// + /// The size of the security key in bytes. Default is 32 bytes. + /// A new instance of . + public static SymmetricSecurityKey GenerateSecurityKey(int byteSize = 32) + { + using var rng = RandomNumberGenerator.Create(); + var randomBytes = new byte[byteSize]; + rng.GetBytes(randomBytes); + var securityKey = new SymmetricSecurityKey(randomBytes); - /// - /// Generates a symmetric security key with the specified byte size. - /// - /// The size of the security key in bytes. Default is 32 bytes. - /// A new instance of . - public static SymmetricSecurityKey GenerateSecurityKey(int byteSize = 32) - { - using var rng = RandomNumberGenerator.Create(); - var randomBytes = new byte[byteSize]; - rng.GetBytes(randomBytes); - var securityKey = new SymmetricSecurityKey(randomBytes); + return securityKey; + } - return securityKey; - } + /// + /// Generates a JWT for the specified claim value. + /// + /// The claim value to encode in the JWT. + /// A JWT as a string. + public string GenerateToken(TClaimValue claimValue) + { + var tokenDescriptor = _factory(claimValue); - /// - /// Generates a JWT for the specified claim value. - /// - /// The claim value to encode in the JWT. - /// A JWT as a string. - public string GenerateToken(TClaimValue claimValue) - { - var tokenDescriptor = _factory(claimValue); + var tokenHandler = new JwtSecurityTokenHandler(); + var token = tokenHandler.CreateToken(tokenDescriptor); + return tokenHandler.WriteToken(token); + } - var tokenHandler = new JwtSecurityTokenHandler(); - var token = tokenHandler.CreateToken(tokenDescriptor); - return tokenHandler.WriteToken(token); - } - - /// - /// Reads and validates a security token from a string representation. - /// - /// The JWT to read. - /// A if the token is valid; otherwise, null. - public JwtSecurityToken? ReadSecurityToken(string token) - { - var tokenHandler = new JwtSecurityTokenHandler(); - return tokenHandler.CanReadToken(token) ? tokenHandler.ReadToken(token) as JwtSecurityToken : null; - } + /// + /// Reads and validates a security token from a string representation. + /// + /// The JWT to read. + /// A if the token is valid; otherwise, null. + public JwtSecurityToken? ReadSecurityToken(string token) + { + var tokenHandler = new JwtSecurityTokenHandler(); + return tokenHandler.CanReadToken(token) ? tokenHandler.ReadToken(token) as JwtSecurityToken : null; } } \ No newline at end of file diff --git a/DigitalData.Core.Application/Key.cs b/DigitalData.Core.Application/Key.cs index d8e352f..93ed02a 100644 --- a/DigitalData.Core.Application/Key.cs +++ b/DigitalData.Core.Application/Key.cs @@ -1,11 +1,11 @@ -namespace DigitalData.Core.Application +namespace DigitalData.Core.Application; + +[Obsolete("Use MediatR")] +public static class Key { - public static class Key - { - public static readonly string EntityDoesNotExist = "EntityDoesNotExist"; - public static readonly string ReadFailed = "ReadFailed"; - public static readonly string UpdateFailed = "UpdateFailed"; - public static readonly string DeletionFailed = "DeletionFailed"; - public static readonly string DirSearcherDisconnected = "DirSearcherDisconnected"; - } + public static readonly string EntityDoesNotExist = "EntityDoesNotExist"; + public static readonly string ReadFailed = "ReadFailed"; + public static readonly string UpdateFailed = "UpdateFailed"; + public static readonly string DeletionFailed = "DeletionFailed"; + public static readonly string DirSearcherDisconnected = "DirSearcherDisconnected"; } \ No newline at end of file diff --git a/DigitalData.Core.Application/ReadService.cs b/DigitalData.Core.Application/ReadService.cs index 245f24b..41ba3a2 100644 --- a/DigitalData.Core.Application/ReadService.cs +++ b/DigitalData.Core.Application/ReadService.cs @@ -1,79 +1,78 @@ using AutoMapper; -using DigitalData.Core.Abstractions; using DigitalData.Core.Application.DTO; using DigitalData.Core.Application.Interfaces; using DigitalData.Core.Application.Interfaces.Repository; -namespace DigitalData.Core.Application +namespace DigitalData.Core.Application; + +/// +/// Provides generic Read (Read and Delete) operations for a specified type of entity. +/// +/// The DTO type for read operations. +/// The entity type. +/// The type of the identifier for the entity. +[Obsolete("Use MediatR")] +public class ReadService : IReadService + where TCRUDRepository : ICRUDRepository where TReadDto : class where TEntity : class { + protected readonly TCRUDRepository _repository; + protected readonly IMapper _mapper; + /// - /// Provides generic Read (Read and Delete) operations for a specified type of entity. + /// Initializes a new instance of the CRUDService class with the specified repository, translation service, and mapper. /// - /// The DTO type for read operations. - /// The entity type. - /// The type of the identifier for the entity. - public class ReadService : IReadService - where TCRUDRepository : ICRUDRepository where TReadDto : class where TEntity : class, IUnique + /// The CRUD repository for accessing the database. + /// The AutoMapper instance for mapping between DTOs and entity objects. + public ReadService(TCRUDRepository repository, IMapper mapper) { - protected readonly TCRUDRepository _repository; - protected readonly IMapper _mapper; - - /// - /// Initializes a new instance of the CRUDService class with the specified repository, translation service, and mapper. - /// - /// The CRUD repository for accessing the database. - /// The AutoMapper instance for mapping between DTOs and entity objects. - public ReadService(TCRUDRepository repository, IMapper mapper) - { - _repository = repository; - _mapper = mapper; - } - - /// - /// Asynchronously reads an entity by its identifier and maps it to a read DTO. - /// - /// The identifier of the entity to read. - /// A service result indicating success or failure, including the read DTO if successful. - public virtual async Task> ReadByIdAsync(TId id) - { - var entity = await _repository.ReadByIdAsync(id); - return entity is null - ? Result.Fail() - : Result.Success(_mapper.Map(entity)); - } - - /// - /// Asynchronously reads all entities and maps them to read DTOs. - /// - /// A service result including a collection of read DTOs. - public virtual async Task>> ReadAllAsync() - { - var entities = await _repository.ReadAllAsync(); - var readDto = _mapper.Map>(entities); - return Result.Success(readDto); - } - - /// - /// Asynchronously deletes an entity by its identifier. - /// - /// The identifier of the entity to delete. - /// A service message indicating success or failure. - public virtual async Task DeleteAsyncById(TId id) - { - TEntity? entity = await _repository.ReadByIdAsync(id); - - if (entity is null) - return Result.Fail(); - - bool isDeleted = await _repository.DeleteAsync(entity); - return isDeleted ? Result.Success() : Result.Fail(); - } - - /// - /// Asynchronously checks if an entity with the specified identifier exists. - /// - /// The identifier of the entity to check. - /// A Task that represents the asynchronous operation. The task result contains a boolean value indicating whether the entity exists. - public virtual async Task HasEntity(TId id) => await _repository.CountAsync(id) > 0; + _repository = repository; + _mapper = mapper; } + + /// + /// Asynchronously reads an entity by its identifier and maps it to a read DTO. + /// + /// The identifier of the entity to read. + /// A service result indicating success or failure, including the read DTO if successful. + public virtual async Task> ReadByIdAsync(TId id) + { + var entity = await _repository.ReadByIdAsync(id); + return entity is null + ? Result.Fail() + : Result.Success(_mapper.Map(entity)); + } + + /// + /// Asynchronously reads all entities and maps them to read DTOs. + /// + /// A service result including a collection of read DTOs. + public virtual async Task>> ReadAllAsync() + { + var entities = await _repository.ReadAllAsync(); + var readDto = _mapper.Map>(entities); + return Result.Success(readDto); + } + + /// + /// Asynchronously deletes an entity by its identifier. + /// + /// The identifier of the entity to delete. + /// A service message indicating success or failure. + public virtual async Task DeleteAsyncById(TId id) + { + TEntity? entity = await _repository.ReadByIdAsync(id); + + if (entity is null) + return Result.Fail(); + + bool isDeleted = await _repository.DeleteAsync(entity); + return isDeleted ? Result.Success() : Result.Fail(); + } + + /// + /// Asynchronously checks if an entity with the specified identifier exists. + /// + /// The identifier of the entity to check. + /// A Task that represents the asynchronous operation. The task result contains a boolean value indicating whether the entity exists. + public virtual async Task HasEntity(TId id) => await _repository.CountAsync(id) > 0; } \ No newline at end of file