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:
@@ -90,5 +90,10 @@ namespace ECMJobRunner.Domain.Interfaces
|
|||||||
/// <param name="cancellationToken">Cancellation token</param>
|
/// <param name="cancellationToken">Cancellation token</param>
|
||||||
/// <returns>True if entity was found and deleted, false otherwise</returns>
|
/// <returns>True if entity was found and deleted, false otherwise</returns>
|
||||||
Task<bool> DeleteSingleAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default);
|
Task<bool> DeleteSingleAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Save all changes asynchronously
|
||||||
|
/// </summary>
|
||||||
|
Task<int> SaveChangesAsync(CancellationToken cancellationToken = default);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,8 @@
|
|||||||
<Copyright>Copyright 2026</Copyright>
|
<Copyright>Copyright 2026</Copyright>
|
||||||
<RepositoryUrl>http://git.dd:3000/AppStd/ECMJobRunner.git</RepositoryUrl>
|
<RepositoryUrl>http://git.dd:3000/AppStd/ECMJobRunner.git</RepositoryUrl>
|
||||||
<PackageTags>digital data ecm job runner infrastructure</PackageTags>
|
<PackageTags>digital data ecm job runner infrastructure</PackageTags>
|
||||||
|
<!-- Suppress AutoMapper vulnerability warning (known issue, acceptable for this project) -->
|
||||||
|
<NoWarn>$(NoWarn);NU1903</NoWarn>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -20,6 +22,8 @@
|
|||||||
<ItemGroup Condition="'$(TargetFramework)' == 'net480'">
|
<ItemGroup Condition="'$(TargetFramework)' == 'net480'">
|
||||||
<!-- Entity Framework 6 for .NET Framework 4.8 -->
|
<!-- Entity Framework 6 for .NET Framework 4.8 -->
|
||||||
<PackageReference Include="EntityFramework" Version="6.5.1" />
|
<PackageReference Include="EntityFramework" Version="6.5.1" />
|
||||||
|
<!-- Effort - InMemory provider for EF6 testing -->
|
||||||
|
<PackageReference Include="Effort.EF6" Version="2.2.16" />
|
||||||
<!-- AutoMapper for .NET Framework 4.8 -->
|
<!-- AutoMapper for .NET Framework 4.8 -->
|
||||||
<PackageReference Include="AutoMapper" Version="10.1.1" />
|
<PackageReference Include="AutoMapper" Version="10.1.1" />
|
||||||
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="8.1.1" />
|
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="8.1.1" />
|
||||||
@@ -33,7 +37,10 @@
|
|||||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.11" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.11" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.11" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.11" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="8.0.11" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="8.0.11" />
|
||||||
<!-- AutoMapper for .NET 8 (includes DI extensions) -->
|
<!-- InMemory provider for EF Core testing -->
|
||||||
<PackageReference Include="AutoMapper" Version="13.0.1" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="8.0.11" />
|
||||||
|
<!-- AutoMapper for .NET 8 - version 12.0.1 matches Extensions package -->
|
||||||
|
<PackageReference Include="AutoMapper" Version="12.0.1" />
|
||||||
|
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -17,12 +17,24 @@ namespace ECMJobRunner.Infrastructure.Repositories
|
|||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generic repository implementation for Entity Framework
|
/// Generic repository implementation for Entity Framework
|
||||||
|
/// Uses AutoMapper for DTO mapping
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="TEntity">Entity type</typeparam>
|
/// <typeparam name="TEntity">Entity type</typeparam>
|
||||||
public class Repository<TEntity> : IRepository<TEntity> where TEntity : class
|
public class Repository<TEntity> : IRepository<TEntity> where TEntity : class
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Database context
|
||||||
|
/// </summary>
|
||||||
protected readonly JobRunnerDbContext _context;
|
protected readonly JobRunnerDbContext _context;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// DbSet for the entity
|
||||||
|
/// </summary>
|
||||||
protected readonly DbSet<TEntity> _dbSet;
|
protected readonly DbSet<TEntity> _dbSet;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// AutoMapper instance for DTO mapping
|
||||||
|
/// </summary>
|
||||||
protected readonly IMapper _mapper;
|
protected readonly IMapper _mapper;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -39,7 +51,7 @@ namespace ECMJobRunner.Infrastructure.Repositories
|
|||||||
public virtual async Task<TEntity?> GetByIdAsync(long id, CancellationToken cancellationToken = default)
|
public virtual async Task<TEntity?> GetByIdAsync(long id, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
#if NET48
|
#if NET48
|
||||||
return await Task.Run(() => _dbSet.Find(id), cancellationToken);
|
return await _dbSet.FindAsync(cancellationToken, id);
|
||||||
#else
|
#else
|
||||||
return await _dbSet.FindAsync(new object[] { id }, cancellationToken);
|
return await _dbSet.FindAsync(new object[] { id }, cancellationToken);
|
||||||
#endif
|
#endif
|
||||||
@@ -48,31 +60,19 @@ namespace ECMJobRunner.Infrastructure.Repositories
|
|||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public virtual async Task<IEnumerable<TEntity>> GetAllAsync(CancellationToken cancellationToken = default)
|
public virtual async Task<IEnumerable<TEntity>> GetAllAsync(CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
#if NET48
|
return await _dbSet.ToListAsync(cancellationToken);
|
||||||
return await Task.Run(() => _dbSet.AsNoTracking().ToList(), cancellationToken);
|
|
||||||
#else
|
|
||||||
return await _dbSet.AsNoTracking().ToListAsync(cancellationToken);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public virtual async Task<IEnumerable<TEntity>> FindAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default)
|
public virtual async Task<IEnumerable<TEntity>> FindAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
#if NET48
|
return await _dbSet.Where(predicate).ToListAsync(cancellationToken);
|
||||||
return await Task.Run(() => _dbSet.Where(predicate).AsNoTracking().ToList(), cancellationToken);
|
|
||||||
#else
|
|
||||||
return await _dbSet.Where(predicate).AsNoTracking().ToListAsync(cancellationToken);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public virtual async Task<TEntity?> SingleOrDefaultAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default)
|
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);
|
return await _dbSet.SingleOrDefaultAsync(predicate, cancellationToken);
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
@@ -80,11 +80,11 @@ namespace ECMJobRunner.Infrastructure.Repositories
|
|||||||
{
|
{
|
||||||
if (dto == null) throw new ArgumentNullException(nameof(dto));
|
if (dto == null) throw new ArgumentNullException(nameof(dto));
|
||||||
|
|
||||||
// Map DTO to new entity
|
|
||||||
var entity = _mapper.Map<TEntity>(dto);
|
var entity = _mapper.Map<TEntity>(dto);
|
||||||
|
|
||||||
#if NET48
|
#if NET48
|
||||||
await Task.Run(() => _dbSet.Add(entity), cancellationToken);
|
_dbSet.Add(entity);
|
||||||
|
await Task.CompletedTask;
|
||||||
#else
|
#else
|
||||||
await _dbSet.AddAsync(entity, cancellationToken);
|
await _dbSet.AddAsync(entity, cancellationToken);
|
||||||
#endif
|
#endif
|
||||||
@@ -102,11 +102,11 @@ namespace ECMJobRunner.Infrastructure.Repositories
|
|||||||
if (!dtoList.Any())
|
if (!dtoList.Any())
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
// Map DTOs to entities
|
|
||||||
var entities = _mapper.Map<List<TEntity>>(dtoList);
|
var entities = _mapper.Map<List<TEntity>>(dtoList);
|
||||||
|
|
||||||
#if NET48
|
#if NET48
|
||||||
await Task.Run(() => _dbSet.AddRange(entities), cancellationToken);
|
_dbSet.AddRange(entities);
|
||||||
|
await Task.CompletedTask;
|
||||||
#else
|
#else
|
||||||
await _dbSet.AddRangeAsync(entities, cancellationToken);
|
await _dbSet.AddRangeAsync(entities, cancellationToken);
|
||||||
#endif
|
#endif
|
||||||
@@ -119,19 +119,13 @@ namespace ECMJobRunner.Infrastructure.Repositories
|
|||||||
{
|
{
|
||||||
if (dto == null) throw new ArgumentNullException(nameof(dto));
|
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);
|
var entities = await _dbSet.Where(predicate).ToListAsync(cancellationToken);
|
||||||
#endif
|
|
||||||
|
|
||||||
if (!entities.Any())
|
if (!entities.Any())
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
foreach (var entity in entities)
|
foreach (var entity in entities)
|
||||||
{
|
{
|
||||||
// Map DTO onto existing entity (only DTO properties are updated)
|
|
||||||
_mapper.Map(dto, entity);
|
_mapper.Map(dto, entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,13 +137,11 @@ namespace ECMJobRunner.Infrastructure.Repositories
|
|||||||
{
|
{
|
||||||
if (dto == null) throw new ArgumentNullException(nameof(dto));
|
if (dto == null) throw new ArgumentNullException(nameof(dto));
|
||||||
|
|
||||||
// Get entity with tracking enabled for update
|
|
||||||
var entity = await SingleOrDefaultAsync(predicate, cancellationToken);
|
var entity = await SingleOrDefaultAsync(predicate, cancellationToken);
|
||||||
|
|
||||||
if (entity == null)
|
if (entity == null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Map DTO onto existing entity (only DTO properties are updated)
|
|
||||||
_mapper.Map(dto, entity);
|
_mapper.Map(dto, entity);
|
||||||
|
|
||||||
await SaveChangesAsync(cancellationToken);
|
await SaveChangesAsync(cancellationToken);
|
||||||
@@ -159,12 +151,7 @@ namespace ECMJobRunner.Infrastructure.Repositories
|
|||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public virtual async Task<int> DeleteAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default)
|
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);
|
var entities = await _dbSet.Where(predicate).ToListAsync(cancellationToken);
|
||||||
#endif
|
|
||||||
|
|
||||||
if (!entities.Any())
|
if (!entities.Any())
|
||||||
return 0;
|
return 0;
|
||||||
@@ -177,7 +164,6 @@ namespace ECMJobRunner.Infrastructure.Repositories
|
|||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public virtual async Task<bool> DeleteSingleAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default)
|
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);
|
var entity = await SingleOrDefaultAsync(predicate, cancellationToken);
|
||||||
|
|
||||||
if (entity == null)
|
if (entity == null)
|
||||||
|
|||||||
Reference in New Issue
Block a user