Refactor Repository and update project dependencies

Added `SaveChangesAsync` to `IRepository` for async persistence.
Introduced AutoMapper and DbContext dependencies in `Repository`.
Simplified EF Core operations by removing `#if NET48` logic.
Replaced `Task.Run` with direct EF Core calls for .NET Framework.
Updated AutoMapper version for .NET 8 and added EF Core testing.
Suppressed AutoMapper vulnerability warning in project file.
Performed general cleanup and improved maintainability.
This commit is contained in:
2026-07-09 15:58:33 +02:00
parent 574e5ed209
commit 9bae0ab95c
3 changed files with 33 additions and 35 deletions

View File

@@ -17,12 +17,24 @@ namespace ECMJobRunner.Infrastructure.Repositories
{
/// <summary>
/// Generic repository implementation for Entity Framework
/// Uses AutoMapper for DTO mapping
/// </summary>
/// <typeparam name="TEntity">Entity type</typeparam>
public class Repository<TEntity> : IRepository<TEntity> where TEntity : class
{
/// <summary>
/// Database context
/// </summary>
protected readonly JobRunnerDbContext _context;
/// <summary>
/// DbSet for the entity
/// </summary>
protected readonly DbSet<TEntity> _dbSet;
/// <summary>
/// AutoMapper instance for DTO mapping
/// </summary>
protected readonly IMapper _mapper;
/// <summary>
@@ -39,7 +51,7 @@ namespace ECMJobRunner.Infrastructure.Repositories
public virtual async Task<TEntity?> GetByIdAsync(long id, CancellationToken cancellationToken = default)
{
#if NET48
return await Task.Run(() => _dbSet.Find(id), cancellationToken);
return await _dbSet.FindAsync(cancellationToken, id);
#else
return await _dbSet.FindAsync(new object[] { id }, cancellationToken);
#endif
@@ -48,31 +60,19 @@ namespace ECMJobRunner.Infrastructure.Repositories
/// <inheritdoc/>
public virtual async Task<IEnumerable<TEntity>> GetAllAsync(CancellationToken cancellationToken = default)
{
#if NET48
return await Task.Run(() => _dbSet.AsNoTracking().ToList(), cancellationToken);
#else
return await _dbSet.AsNoTracking().ToListAsync(cancellationToken);
#endif
return await _dbSet.ToListAsync(cancellationToken);
}
/// <inheritdoc/>
public virtual async Task<IEnumerable<TEntity>> FindAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default)
{
#if NET48
return await Task.Run(() => _dbSet.Where(predicate).AsNoTracking().ToList(), cancellationToken);
#else
return await _dbSet.Where(predicate).AsNoTracking().ToListAsync(cancellationToken);
#endif
return await _dbSet.Where(predicate).ToListAsync(cancellationToken);
}
/// <inheritdoc/>
public virtual async Task<TEntity?> SingleOrDefaultAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default)
{
#if NET48
return await Task.Run(() => _dbSet.SingleOrDefault(predicate), cancellationToken);
#else
return await _dbSet.SingleOrDefaultAsync(predicate, cancellationToken);
#endif
}
/// <inheritdoc/>
@@ -80,11 +80,11 @@ namespace ECMJobRunner.Infrastructure.Repositories
{
if (dto == null) throw new ArgumentNullException(nameof(dto));
// Map DTO to new entity
var entity = _mapper.Map<TEntity>(dto);
#if NET48
await Task.Run(() => _dbSet.Add(entity), cancellationToken);
_dbSet.Add(entity);
await Task.CompletedTask;
#else
await _dbSet.AddAsync(entity, cancellationToken);
#endif
@@ -102,11 +102,11 @@ namespace ECMJobRunner.Infrastructure.Repositories
if (!dtoList.Any())
return 0;
// Map DTOs to entities
var entities = _mapper.Map<List<TEntity>>(dtoList);
#if NET48
await Task.Run(() => _dbSet.AddRange(entities), cancellationToken);
_dbSet.AddRange(entities);
await Task.CompletedTask;
#else
await _dbSet.AddRangeAsync(entities, cancellationToken);
#endif
@@ -119,19 +119,13 @@ namespace ECMJobRunner.Infrastructure.Repositories
{
if (dto == null) throw new ArgumentNullException(nameof(dto));
// Get entities with tracking enabled for update
#if NET48
var entities = await Task.Run(() => _dbSet.Where(predicate).ToList(), cancellationToken);
#else
var entities = await _dbSet.Where(predicate).ToListAsync(cancellationToken);
#endif
if (!entities.Any())
return 0;
foreach (var entity in entities)
{
// Map DTO onto existing entity (only DTO properties are updated)
_mapper.Map(dto, entity);
}
@@ -143,13 +137,11 @@ namespace ECMJobRunner.Infrastructure.Repositories
{
if (dto == null) throw new ArgumentNullException(nameof(dto));
// Get entity with tracking enabled for update
var entity = await SingleOrDefaultAsync(predicate, cancellationToken);
if (entity == null)
return false;
// Map DTO onto existing entity (only DTO properties are updated)
_mapper.Map(dto, entity);
await SaveChangesAsync(cancellationToken);
@@ -159,12 +151,7 @@ namespace ECMJobRunner.Infrastructure.Repositories
/// <inheritdoc/>
public virtual async Task<int> DeleteAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default)
{
// Get entities with tracking enabled for delete
#if NET48
var entities = await Task.Run(() => _dbSet.Where(predicate).ToList(), cancellationToken);
#else
var entities = await _dbSet.Where(predicate).ToListAsync(cancellationToken);
#endif
if (!entities.Any())
return 0;
@@ -177,7 +164,6 @@ namespace ECMJobRunner.Infrastructure.Repositories
/// <inheritdoc/>
public virtual async Task<bool> DeleteSingleAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default)
{
// Get entity with tracking enabled for delete
var entity = await SingleOrDefaultAsync(predicate, cancellationToken);
if (entity == null)