Add Infrastructure layer with EF6/EF Core support
Introduced a robust Infrastructure layer for the ECMJobRunner system: - Added `AGENTS.md` with detailed project documentation. - Implemented generic repository and unit-of-work patterns. - Added `CfgProfileRepository`, `ProfileSqlJobRepository`, and `ProfileHistoryRepository`. - Integrated AutoMapper for DTO-to-entity mapping. - Added multi-framework support for .NET Framework 4.8 (EF6) and .NET 8.0 (EF Core) using conditional compilation. - Updated `ECMJobRunner.Infrastructure.csproj` with metadata fixes and dependencies. - Introduced dependency injection extension for .NET 8.0. - Enhanced project structure and database context with entity mappings.
This commit is contained in:
202
ECMJobRunner.Infrastructure/Repositories/Repository.cs
Normal file
202
ECMJobRunner.Infrastructure/Repositories/Repository.cs
Normal file
@@ -0,0 +1,202 @@
|
||||
#if NET48
|
||||
using System.Data.Entity;
|
||||
#else
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
#endif
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using AutoMapper;
|
||||
using ECMJobRunner.Domain.Interfaces;
|
||||
using ECMJobRunner.Infrastructure.Data;
|
||||
|
||||
namespace ECMJobRunner.Infrastructure.Repositories
|
||||
{
|
||||
/// <summary>
|
||||
/// Generic repository implementation for Entity Framework
|
||||
/// </summary>
|
||||
/// <typeparam name="TEntity">Entity type</typeparam>
|
||||
public class Repository<TEntity> : IRepository<TEntity> where TEntity : class
|
||||
{
|
||||
protected readonly JobRunnerDbContext _context;
|
||||
protected readonly DbSet<TEntity> _dbSet;
|
||||
protected readonly IMapper _mapper;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public Repository(JobRunnerDbContext context, IMapper mapper)
|
||||
{
|
||||
_context = context ?? throw new ArgumentNullException(nameof(context));
|
||||
_mapper = mapper ?? throw new ArgumentNullException(nameof(mapper));
|
||||
_dbSet = context.Set<TEntity>();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual async Task<TEntity?> GetByIdAsync(long id, CancellationToken cancellationToken = default)
|
||||
{
|
||||
#if NET48
|
||||
return await Task.Run(() => _dbSet.Find(id), cancellationToken);
|
||||
#else
|
||||
return await _dbSet.FindAsync(new object[] { id }, cancellationToken);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <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
|
||||
}
|
||||
|
||||
/// <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
|
||||
}
|
||||
|
||||
/// <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/>
|
||||
public virtual async Task<TEntity> AddAsync<TDto>(TDto dto, CancellationToken cancellationToken = default) where TDto : class
|
||||
{
|
||||
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);
|
||||
#else
|
||||
await _dbSet.AddAsync(entity, cancellationToken);
|
||||
#endif
|
||||
|
||||
await SaveChangesAsync(cancellationToken);
|
||||
return entity;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual async Task<int> AddRangeAsync<TDto>(IEnumerable<TDto> dtos, CancellationToken cancellationToken = default) where TDto : class
|
||||
{
|
||||
if (dtos == null) throw new ArgumentNullException(nameof(dtos));
|
||||
|
||||
var dtoList = dtos.ToList();
|
||||
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);
|
||||
#else
|
||||
await _dbSet.AddRangeAsync(entities, cancellationToken);
|
||||
#endif
|
||||
|
||||
return await SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual async Task<int> UpdateAsync<TDto>(Expression<Func<TEntity, bool>> predicate, TDto dto, CancellationToken cancellationToken = default) where TDto : class
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
return await SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual async Task<bool> UpdateSingleAsync<TDto>(Expression<Func<TEntity, bool>> predicate, TDto dto, CancellationToken cancellationToken = default) where TDto : class
|
||||
{
|
||||
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);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <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;
|
||||
|
||||
_dbSet.RemoveRange(entities);
|
||||
|
||||
return await SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
|
||||
/// <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)
|
||||
return false;
|
||||
|
||||
_dbSet.Remove(entity);
|
||||
|
||||
await SaveChangesAsync(cancellationToken);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
#if NET48
|
||||
return await _context.SaveChangesAsync();
|
||||
#else
|
||||
return await _context.SaveChangesAsync(cancellationToken);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user