20 Commits

Author SHA1 Message Date
Developer 02
b6ac303c96 fix: HasEntity-Methode auf CountAsync aktualisiert
Die Implementierung der HasEntity-Methode wurde ersetzt, um die CountAsync-Methode des Repositories zu verwenden. Dies sorgt für eine effizientere Überprüfung der Existenz von Entitäten.
2024-09-11 10:54:41 +02:00
Developer 02
406a41b91f feat: CountAsync-Methode zum Repository-Interface und zur Implementierung hinzugefügt
Eine neue asynchrone CountAsync-Methode wurde zum Repository-Interface und zur Implementierung hinzugefügt. Diese Methode zählt die Anzahl der Entitäten mit einer bestimmten ID in der Datenbank.
2024-09-11 10:52:56 +02:00
Developer 02
adfb0daf7d feat: Füge CountAsync Methode zum ICRUDRepository Interface hinzu 2024-09-11 10:07:20 +02:00
Developer 02
a6d554fbc2 refactor: Entfernte die HandleException Methode aus der CRUDService Klasse und dem Interface
- Die Methode `HandleException` wurde aus der `CRUDService` Klasse entfernt.
- Die Methode wurde ebenfalls aus dem `ICRUDService` Interface entfernt.
2024-09-11 10:05:15 +02:00
Developer 02
c6199cc0be refactor: Entfernte nicht benötigte _keyPropertyInfo und aktualisierte CreateAsync Methode
- Entfernte `_keyPropertyInfo` und die zugehörige Methode `KeyValueOf`, da sie nicht mehr benötigt wird.
- Aktualisierte `CreateAsync` Methode, um direkt `createdEntity.Id` zurückzugeben.
2024-09-11 10:03:04 +02:00
Developer 02
ed2a591317 refactor: Kommentar zur Dokumentation aktualisieren 2024-09-11 09:57:42 +02:00
Developer 02
f6d5305c22 fix: CRUDRepository Konstruktor aktualisiert, um DbSet<TEntity> als Parameter zu akzeptieren
- `CRUDRepository` Konstruktor geändert, um `DbSet<TEntity>` direkt zu akzeptieren.
- Erlaubt flexible Handhabung von `DbSet`-Instanzen beim Erstellen des Repositories.
2024-09-11 09:56:04 +02:00
Developer 02
a6230419d8 refactor: ICRUDService, CRUDService, CRUDRepository und ICRUDRepository um IUnique<TId> Einschränkung zu erzwingen
- `IUnique<TId>` Einschränkung zu `TEntity` in den Schnittstellen `ICRUDService`, `CRUDService`, `ICRUDRepository` und `CRUDRepository` hinzugefügt.
- Relevanten Code aktualisiert, um die neue Einschränkung zu berücksichtigen und sicherzustellen, dass Entitäten `IUnique<TId>` implementieren.
2024-09-11 09:54:26 +02:00
Developer 02
b6cd520b72 refactor: CRUDService-Methoden auf vereinfachte Map-Methode umstellen
- `MapOrThrow` auf `Map` für das Mapping von DTOs in `CRUDService` geändert.
- Methoden-Signaturen und interne Logik angepasst, um die aktualisierten `Map`-Methoden zu verwenden.
- Ausnahmebehandlung und ID-Abruffunktionalität beibehalten.
2024-09-11 09:47:52 +02:00
Developer 02
68bfe93cf2 chore: MapOrThrow Methode in AutoMapperExtension als veraltet markieren
- Methode `MapOrThrow` als veraltet markiert.
- Empfohlen, stattdessen `mapper.Map<T>` direkt zu verwenden.
- Zusammenfassung aktualisiert, um die Veraltung widerzuspiegeln und Hinweise zur empfohlenen Methode zu geben.
2024-09-11 09:45:22 +02:00
Developer 02
e6849cd9c9 Fix: Überprüfung hinzugefügt, ob die Entität in UpdateAsync vorhanden ist
- Eine Überprüfung hinzugefügt, um sicherzustellen, dass die Entität vor dem Aktualisieren existiert.
- Eine Warnung wird protokolliert, wenn die Entität im Aktualisierungsprozess nicht gefunden wird.
- Das `updateDto` wird auf die bestehende Entität gemappt, anstatt eine neue zu erstellen.
2024-09-11 09:43:01 +02:00
Developer 02
d59350174c Refactor: Erzwinge IUnique<TId> Einschränkung für TUpdateDto in CRUDService
- Aktualisiert: `ICRUDService` und `CRUDService`, um die `IUnique<TId>`-Einschränkung für `TUpdateDto` sicherzustellen, dass Update-Datenübertragungsobjekte eine eindeutige Kennung enthalten.
2024-09-11 09:29:21 +02:00
Developer 02
5f18ccd2bd feat: Hinzufügen des IUnique<T>-Interfaces zur Implementierung einer eindeutigen Kennung 2024-09-11 09:26:07 +02:00
Developer 02
58d879aec5 Revert "feat: Hinzufügen des IUnique<T>-Interfaces zur Implementierung einer eindeutigen Kennung"
This reverts commit c9d07ce7bf.
2024-09-11 09:24:48 +02:00
Developer 02
c9d07ce7bf feat: Hinzufügen des IUnique<T>-Interfaces zur Implementierung einer eindeutigen Kennung 2024-09-11 09:22:48 +02:00
Developer 02
bb39b97d1e refactor: Umbenennung der ReadAll-Methode in ReadOnly im CRUDRepository 2024-09-10 11:21:21 +02:00
Developer 02
b91769d931 refactor: ReadAll-Methode aus allen Schnittstellen entfernt, da sie unnötig war. Diese Änderung verbessert die Klarheit der Schnittstellen und entspricht den Best Practices. 2024-09-10 11:17:32 +02:00
Developer 02
ee5668a5cb feat: ReadAll-Methode in ICRUDRepository und CRUDRepository implementieren
- ReadAll-Methode zur ICRUDRepository-Schnittstelle hinzugefügt, um die Abfrage aller Entitäten zu ermöglichen.
- ReadAll-Methode in der CRUDRepository-Klasse implementiert, um IQueryable<TEntity> für weitere Abfragen und Filterung bereitzustellen.
2024-09-09 17:44:18 +02:00
Developer 02
67a3c598b1 chore: Increment version number to 1.0.0.1 for package update 2024-08-30 11:51:00 +02:00
Developer 02
ceb8858dc9 fix: 'NotFound'-Flag zur Flag-Enum hinzufügen, um fehlende Ressourcen oder Operationen anzuzeigen 2024-08-29 16:15:59 +02:00
9 changed files with 86 additions and 70 deletions

View File

@@ -4,7 +4,7 @@ using DigitalData.Core.DTO;
namespace DigitalData.Core.Abstractions.Application
{
public interface ICRUDService<TCreateDto, TReadDto, TUpdateDto, TEntity, TId>
where TCreateDto : class where TReadDto : class where TUpdateDto : class where TEntity : class
where TCreateDto : class where TReadDto : class where TUpdateDto : IUnique<TId> where TEntity : class, IUnique<TId>
{
Task<DataResult<TId>> CreateAsync(TCreateDto createDto);
@@ -45,16 +45,5 @@ namespace DigitalData.Core.Abstractions.Application
/// <param name="id">The identifier of the entity to check.</param>
/// <returns>A task that represents the asynchronous operation. The task result contains a boolean value indicating whether the entity exists.</returns>
Task<bool> HasEntity(TId id);
/// <summary>
/// Handles exceptions that occur within service actions. This method should log the exception
/// and return an String that contains information about the error, which can then be sent to the client.
/// The implementation should determine the appropriate level of detail to include in the error message
/// based on security and usability considerations.
/// </summary>
/// <param name="ex">The exception that occurred during the controller action.</param>
/// <returns>An string instance representing the outcome of the error handling process.
/// This includes a flag indicating the operation was unsuccessful and any relevant error messages.</returns>
string HandleException(Exception ex);
}
}

View File

@@ -0,0 +1,7 @@
namespace DigitalData.Core.Abstractions
{
public interface IUnique<T>
{
public T Id { get; }
}
}

View File

@@ -5,7 +5,7 @@
/// </summary>
/// <typeparam name="TEntity">The type of the entity this repository works with.</typeparam>
/// <typeparam name="TId">The type of the identifier for the entity.</typeparam>
public interface ICRUDRepository<TEntity, TId> where TEntity : class
public interface ICRUDRepository<TEntity, TId> where TEntity : class, IUnique<TId>
{
/// <summary>
/// Adds a new entity to the repository.
@@ -40,5 +40,23 @@
/// <param name="entity">The entity to delete.</param>
/// <returns>If entity is deleted, return true othwerwise return false.</returns>
Task<bool> DeleteAsync(TEntity entity);
/// <summary>
/// Asynchronously counts all entities in the repository.
/// </summary>
/// <returns>The total number of entities in the repository.</returns>
Task<int> CountAsync();
/// <summary>
/// Asynchronously counts the number of entities in the repository that match a specific identifier.
/// </summary>
/// <param name="id">The identifier of the entities to count.</param>
/// <returns>The number of entities with the specified identifier.</returns>
/// <remarks>
/// This method provides a count of entities in the database that match the given identifier.
/// If there are multiple entities with the same identifier, they will all be counted.
/// The default implementation assumes that the identifier is unique for each entity.
/// </remarks>
Task<int> CountAsync(TId id);
}
}

View File

@@ -1,9 +1,9 @@
using DigitalData.Core.Abstractions.Application;
using DigitalData.Core.Abstractions.Infrastructure;
using AutoMapper;
using System.Reflection;
using System.ComponentModel.DataAnnotations;
using DigitalData.Core.DTO;
using DigitalData.Core.Abstractions;
using Microsoft.Extensions.Logging;
namespace DigitalData.Core.Application
{
@@ -16,11 +16,10 @@ namespace DigitalData.Core.Application
/// <typeparam name="TEntity">The entity type.</typeparam>
/// <typeparam name="TId">The type of the identifier for the entity.</typeparam>
public class CRUDService<TCRUDRepository, TCreateDto, TReadDto, TUpdateDto, TEntity, TId> : ICRUDService<TCreateDto, TReadDto, TUpdateDto, TEntity, TId>
where TCRUDRepository : ICRUDRepository<TEntity, TId> where TCreateDto : class where TReadDto : class where TUpdateDto : class where TEntity : class
where TCRUDRepository : ICRUDRepository<TEntity, TId> where TCreateDto : class where TReadDto : class where TUpdateDto : IUnique<TId> where TEntity : class, IUnique<TId>
{
protected readonly TCRUDRepository _repository;
protected readonly IMapper _mapper;
protected readonly PropertyInfo? _keyPropertyInfo;
/// <summary>
/// Initializes a new instance of the CRUDService class with the specified repository, translation service, and mapper.
@@ -31,9 +30,6 @@ namespace DigitalData.Core.Application
{
_repository = repository;
_mapper = mapper;
_keyPropertyInfo = typeof(TEntity).GetProperties()
.FirstOrDefault(prop => Attribute.IsDefined(prop, typeof(KeyAttribute)));
}
/// <summary>
@@ -43,9 +39,9 @@ namespace DigitalData.Core.Application
/// <returns>A service result indicating success or failure, including the entity DTO.</returns>
public virtual async Task<DataResult<TId>> CreateAsync(TCreateDto createDto)
{
var entity = _mapper.MapOrThrow<TEntity>(createDto);
var entity = _mapper.Map<TEntity>(createDto);
var createdEntity = await _repository.CreateAsync(entity);
return createdEntity is null ? Result.Fail<TId>() : Result.Success(KeyValueOf(createdEntity));
return createdEntity is null ? Result.Fail<TId>() : Result.Success(createdEntity.Id);
}
/// <summary>
@@ -58,7 +54,7 @@ namespace DigitalData.Core.Application
var entity = await _repository.ReadByIdAsync(id);
return entity is null
? Result.Fail<TReadDto>()
: Result.Success(_mapper.MapOrThrow<TReadDto>(entity));
: Result.Success(_mapper.Map<TReadDto>(entity));
}
/// <summary>
@@ -68,7 +64,7 @@ namespace DigitalData.Core.Application
public virtual async Task<DataResult<IEnumerable<TReadDto>>> ReadAllAsync()
{
var entities = await _repository.ReadAllAsync();
var readDto = _mapper.MapOrThrow<IEnumerable<TReadDto>>(entities);
var readDto = _mapper.Map<IEnumerable<TReadDto>>(entities);
return Result.Success(readDto);
}
@@ -79,9 +75,16 @@ namespace DigitalData.Core.Application
/// <returns>A service message indicating success or failure.</returns>
public virtual async Task<Result> UpdateAsync(TUpdateDto updateDto)
{
var entity = _mapper.MapOrThrow<TEntity>(updateDto);
bool isUpdated = await _repository.UpdateAsync(entity);
return isUpdated ? Result.Success() : Result.Fail();
var currentEntitiy = await _repository.ReadByIdAsync(updateDto.Id);
if (currentEntitiy is null)
return Result.Fail().Notice(LogLevel.Warning, Flag.NotFound, $"{updateDto.Id} is not found in update process of {GetType()} entity.");
var entity = _mapper.Map(updateDto, currentEntitiy);
return await _repository.UpdateAsync(entity)
? Result.Success()
: Result.Fail();
}
/// <summary>
@@ -105,38 +108,6 @@ namespace DigitalData.Core.Application
/// </summary>
/// <param name="id">The identifier of the entity to check.</param>
/// <returns>A Task that represents the asynchronous operation. The task result contains a boolean value indicating whether the entity exists.</returns>
public virtual async Task<bool> HasEntity(TId id)
{
var entity = await _repository.ReadByIdAsync(id);
return entity is not null;
}
/// <summary>
/// Retrieves the ID value of an entity based on the defined [Key] attribute.
/// </summary>
/// <param name="entity">The entity from which to extract the ID.</param>
/// <returns>The ID of the entity.</returns>
protected virtual TId KeyValueOf(TEntity entity)
{
if (_keyPropertyInfo is null)
throw new InvalidOperationException($"No property with [Key] attribute found on {typeof(TEntity).Name} entity.");
object idObj = _keyPropertyInfo?.GetValue(entity) ?? throw new InvalidOperationException($"The ID property of {typeof(TEntity).Name} entity cannot be null.");
if (idObj is TId id)
return id;
else
throw new InvalidCastException($"The ID of {typeof(TEntity).Name} entity must be type of {typeof(TId).Name}, but it is type of {idObj.GetType().Name}.");
}
/// <summary>
/// Handles exceptions that occur during CRUD operations, providing a structured string.
/// </summary>
/// <param name="ex">The exception that was caught during CRUD operations.</param>
/// <returns>A <see cref="IServiceMessage"/> containing information about the failure, including a user-friendly error message and additional error details.</returns>
public virtual string HandleException(Exception ex)
{
return $"An unexpected error occurred on the server side. Please inform the IT support team.\n{ex.GetType().Name}\n{ex.Message}";
}
public virtual async Task<bool> HasEntity(TId id) => await _repository.CountAsync(id) > 0;
}
}

View File

@@ -4,6 +4,7 @@ namespace DigitalData.Core.DTO
{
public static class AutoMapperExtension
{
[Obsolete("use mapper.Map<T>")]
/// <summary>
/// Maps a source object to a destination object, or throws an exception if the mapping result is null.
/// </summary>
@@ -19,4 +20,4 @@ namespace DigitalData.Core.DTO
"Hint: Ensure that the AutoMapper profile configuration for this mapping is correct.");
}
}
}
}

View File

@@ -7,7 +7,7 @@
<Description>This package provides Data Transfer Object (DTO) implementations and related utilities. It includes generic result handling, DTO extension methods, cookie consent settings management, and AutoMapper integration for robust object mapping, all adhering to Clean Architecture principles to ensure separation of concerns and maintainability.</Description>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<PackageId>DigitalData.Core.DTO</PackageId>
<Version>1.0.0</Version>
<Version>1.0.0.1</Version>
<Authors>Digital Data GmbH</Authors>
<Company>Digital Data GmbH</Company>
<Product>DigitalData.Core.DTO</Product>

View File

@@ -39,6 +39,12 @@
/// Indicates that either a possible security breach, a possible data integrity issue, or both have been detected during the service operation.
/// This flag is used when it is uncertain whether the issue is related to security, data integrity, or both.
/// </summary>
PossibleSecurityBreachOrDataIntegrity
PossibleSecurityBreachOrDataIntegrity,
/// <summary>
/// Indicates that the requested resource or operation could not be found.
/// This flag is used when the specified item or condition does not exist or is unavailable.
/// </summary>
NotFound
}
}

View File

@@ -1,4 +1,5 @@
using DigitalData.Core.Abstractions.Infrastructure;
using DigitalData.Core.Abstractions;
using DigitalData.Core.Abstractions.Infrastructure;
using Microsoft.EntityFrameworkCore;
namespace DigitalData.Core.Infrastructure
@@ -14,7 +15,7 @@ namespace DigitalData.Core.Infrastructure
/// It leverages the EF Core's DbContext and DbSet to perform these operations.
/// </remarks>
public class CRUDRepository<TEntity, TId, TDbContext> : ICRUDRepository<TEntity, TId>
where TEntity : class
where TEntity : class, IUnique<TId>
where TDbContext : DbContext
{
protected readonly TDbContext _dbContext;
@@ -24,10 +25,11 @@ namespace DigitalData.Core.Infrastructure
/// Initializes a new instance of the CRUDRepository with the specified DbContext.
/// </summary>
/// <param name="dbContext">The DbContext instance to be used by the repository.</param>
public CRUDRepository(TDbContext dbContext)
/// <param name="dbSet">The DbSet instance to be used by the repository.</param>
public CRUDRepository(TDbContext dbContext, DbSet<TEntity> dbSet)
{
_dbContext = dbContext;
_dbSet = dbContext.Set<TEntity>();
_dbSet = dbSet;
}
/// <summary>
@@ -49,11 +51,21 @@ namespace DigitalData.Core.Infrastructure
/// <returns>The entity found, or null if no entity is found with the specified identifier.</returns>
public virtual async Task<TEntity?> ReadByIdAsync(TId id) => await _dbSet.FindAsync(id);
/// <summary>
/// Retrieves all entities of type <typeparamref name="TEntity"/> from the database.
/// </summary>
/// <remarks>
/// This method returns an <see cref="IQueryable{TEntity}"/> of all entities in the database.
/// The result is not tracked by the context, which improves performance when you only need to read data without making modifications.
/// </remarks>
/// <returns>An <see cref="IQueryable{TEntity}"/> containing all entities of type <typeparamref name="TEntity"/>.</returns>
protected virtual IQueryable<TEntity> ReadOnly() => _dbSet.AsNoTracking();
/// <summary>
/// Asynchronously retrieves all entities of type TEntity.
/// </summary>
/// <returns>An enumerable of all entities in the database.</returns>
public virtual async Task<IEnumerable<TEntity>> ReadAllAsync() => await _dbSet.ToListAsync();
public virtual async Task<IEnumerable<TEntity>> ReadAllAsync() => await ReadOnly().ToListAsync();
/// <summary>
/// Asynchronously updates an existing entity in the repository.
@@ -84,5 +96,17 @@ namespace DigitalData.Core.Infrastructure
/// </summary>
/// <returns>The total number of entities in the repository.</returns>
public virtual async Task<int> CountAsync() => await _dbSet.CountAsync();
/// <summary>
/// Asynchronously counts the number of entities in the repository that match a specific identifier.
/// </summary>
/// <param name="id">The identifier of the entities to count.</param>
/// <returns>The number of entities with the specified identifier.</returns>
/// <remarks>
/// This method provides a count of entities in the database that match the given identifier.
/// If there are multiple entities with the same identifier, they will all be counted.
/// The default implementation assumes that the identifier is unique for each entity.
/// </remarks>
public virtual async Task<int> CountAsync(TId id) => await _dbSet.Where(e => e.Id!.Equals(id)).CountAsync();
}
}

View File

@@ -43,8 +43,8 @@ Global
{B54DEF90-C30C-44EA-9875-76F1B330CBB7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B54DEF90-C30C-44EA-9875-76F1B330CBB7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B54DEF90-C30C-44EA-9875-76F1B330CBB7}.Release|Any CPU.Build.0 = Release|Any CPU
{0B051A5F-BD38-47D1-BAFF-D44BA30D3FB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0B051A5F-BD38-47D1-BAFF-D44BA30D3FB7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0B051A5F-BD38-47D1-BAFF-D44BA30D3FB7}.Debug|Any CPU.ActiveCfg = Release|Any CPU
{0B051A5F-BD38-47D1-BAFF-D44BA30D3FB7}.Debug|Any CPU.Build.0 = Release|Any CPU
{0B051A5F-BD38-47D1-BAFF-D44BA30D3FB7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0B051A5F-BD38-47D1-BAFF-D44BA30D3FB7}.Release|Any CPU.Build.0 = Release|Any CPU
{6A80FFEC-9B83-40A7-8C78-124440B48B33}.Debug|Any CPU.ActiveCfg = Debug|Any CPU