refactor: modernized GenericRepository and UnitOfWork with newer code from Roma
This commit is contained in:
76
Database/GenericRepository/GenericRepository.cs
Normal file
76
Database/GenericRepository/GenericRepository.cs
Normal file
@@ -0,0 +1,76 @@
|
||||
using System.Linq.Expressions;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace GamificationService.Database.GenericRepository;
|
||||
|
||||
public class GenericRepository<TEntity> : IGenericRepository<TEntity> where TEntity : class
|
||||
{
|
||||
private readonly ApplicationContext _context;
|
||||
private readonly DbSet<TEntity> _dbSet;
|
||||
|
||||
public GenericRepository(ApplicationContext context)
|
||||
{
|
||||
_context = context;
|
||||
_dbSet = context.Set<TEntity>();
|
||||
}
|
||||
|
||||
public virtual IQueryable<TEntity> Get(
|
||||
Expression<Func<TEntity, bool>>? filter = null,
|
||||
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>>? orderBy = null,
|
||||
params Expression<Func<TEntity, object>>[] includes)
|
||||
{
|
||||
IQueryable<TEntity> query = _dbSet;
|
||||
|
||||
if (filter != null)
|
||||
{
|
||||
query = query.Where(filter);
|
||||
}
|
||||
|
||||
if (includes != null)
|
||||
{
|
||||
foreach (var include in includes)
|
||||
{
|
||||
query = query.Include(include);
|
||||
}
|
||||
}
|
||||
|
||||
return orderBy != null ? orderBy(query) : query;
|
||||
}
|
||||
|
||||
public virtual TEntity? GetById(object id) => _dbSet.Find(id);
|
||||
|
||||
public virtual async Task<TEntity?> GetByIdAsync(object id) =>
|
||||
await _dbSet.FindAsync(id);
|
||||
|
||||
public virtual void Add(TEntity entity) => _dbSet.Add(entity);
|
||||
|
||||
public virtual void AddRange(IEnumerable<TEntity> entities) => _dbSet.AddRange(entities);
|
||||
|
||||
public virtual async Task AddAsync(TEntity entity) => await _dbSet.AddAsync(entity);
|
||||
|
||||
public virtual void Delete(object id)
|
||||
{
|
||||
var entity = _dbSet.Find(id);
|
||||
if (entity == null)
|
||||
{
|
||||
throw new KeyNotFoundException($"Entity of type {typeof(TEntity).Name} with id '{id}' was not found.");
|
||||
}
|
||||
Delete(entity);
|
||||
}
|
||||
|
||||
public virtual void DeleteRange(IEnumerable<TEntity> entities) => _dbSet.RemoveRange(entities);
|
||||
|
||||
public virtual void Delete(TEntity entity)
|
||||
{
|
||||
if (_context.Entry(entity).State == EntityState.Detached)
|
||||
{
|
||||
_dbSet.Attach(entity);
|
||||
}
|
||||
_dbSet.Remove(entity);
|
||||
}
|
||||
|
||||
public virtual void Update(TEntity entity)
|
||||
{
|
||||
_dbSet.Update(entity);
|
||||
}
|
||||
}
|
||||
24
Database/GenericRepository/IGenericRepository.cs
Normal file
24
Database/GenericRepository/IGenericRepository.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace GamificationService.Database.GenericRepository;
|
||||
|
||||
public interface IGenericRepository<TEntity> where TEntity : class
|
||||
{
|
||||
IQueryable<TEntity> Get(
|
||||
Expression<Func<TEntity, bool>>? filter = null,
|
||||
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>>? orderBy = null,
|
||||
params Expression<Func<TEntity, object>>[] includes);
|
||||
|
||||
TEntity? GetById(object id);
|
||||
Task<TEntity?> GetByIdAsync(object id);
|
||||
|
||||
void Add(TEntity entity);
|
||||
void AddRange(IEnumerable<TEntity> entities);
|
||||
Task AddAsync(TEntity entity);
|
||||
|
||||
void Delete(object id);
|
||||
void DeleteRange(IEnumerable<TEntity> entities);
|
||||
void Delete(TEntity entity);
|
||||
|
||||
void Update(TEntity entity);
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
using System.Linq.Expressions;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace GamificationService.Database.Repositories;
|
||||
|
||||
public class GenericRepository<TEntity> where TEntity : class
|
||||
{
|
||||
internal ApplicationContext context;
|
||||
internal DbSet<TEntity> dbSet;
|
||||
|
||||
public GenericRepository(ApplicationContext context)
|
||||
{
|
||||
this.context = context;
|
||||
this.dbSet = context.Set<TEntity>();
|
||||
}
|
||||
|
||||
public virtual IQueryable<TEntity> Get(
|
||||
Expression<Func<TEntity, bool>>? filter = null,
|
||||
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>>? orderBy = null,
|
||||
string includeProperties = "")
|
||||
{
|
||||
IQueryable<TEntity> query = dbSet;
|
||||
|
||||
if (filter != null)
|
||||
{
|
||||
query = query.Where(filter);
|
||||
}
|
||||
|
||||
foreach (var includeProperty in includeProperties.Split
|
||||
(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
|
||||
{
|
||||
query = query.Include(includeProperty);
|
||||
}
|
||||
|
||||
if (orderBy != null)
|
||||
{
|
||||
return orderBy(query);
|
||||
}
|
||||
else
|
||||
{
|
||||
return query;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual TEntity? GetByID(object id)
|
||||
{
|
||||
return dbSet.Find(id);
|
||||
}
|
||||
|
||||
public async virtual Task<TEntity?> GetByIDAsync(object id)
|
||||
{
|
||||
return await dbSet.FindAsync(id);
|
||||
}
|
||||
public virtual void Insert(TEntity entity)
|
||||
{
|
||||
dbSet.Add(entity);
|
||||
}
|
||||
public virtual void InsertRange(IEnumerable<TEntity> entities)
|
||||
{
|
||||
dbSet.AddRange(entities);
|
||||
}
|
||||
public async virtual Task InsertAsync(TEntity entity)
|
||||
{
|
||||
await dbSet.AddAsync(entity);
|
||||
}
|
||||
public virtual void Delete(object id)
|
||||
{
|
||||
TEntity? entityToDelete = dbSet.Find(id);
|
||||
|
||||
if (entityToDelete == null)
|
||||
{
|
||||
// It's probably a good idea to throw an error here
|
||||
// but I'm leaving it as is for now
|
||||
return;
|
||||
}
|
||||
|
||||
Delete(entityToDelete);
|
||||
}
|
||||
public virtual void DeleteRange(IEnumerable<TEntity> entities)
|
||||
{
|
||||
dbSet.RemoveRange(entities);
|
||||
}
|
||||
public virtual void Delete(TEntity entityToDelete)
|
||||
{
|
||||
if (context.Entry(entityToDelete).State == EntityState.Detached)
|
||||
{
|
||||
dbSet.Attach(entityToDelete);
|
||||
}
|
||||
dbSet.Remove(entityToDelete);
|
||||
}
|
||||
|
||||
public virtual void Update(TEntity entityToUpdate)
|
||||
{
|
||||
dbSet.Attach(entityToUpdate);
|
||||
context.Entry(entityToUpdate).State = EntityState.Modified;
|
||||
}
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
using Microsoft.EntityFrameworkCore.Storage;
|
||||
|
||||
namespace GamificationService.Database.Repositories;
|
||||
|
||||
public class UnitOfWork : IDisposable
|
||||
{
|
||||
#region fields
|
||||
|
||||
protected ApplicationContext _context;
|
||||
private IDbContextTransaction? _transaction;
|
||||
|
||||
#endregion
|
||||
|
||||
public UnitOfWork(ApplicationContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public bool Save()
|
||||
{
|
||||
return _context.SaveChanges() > 0;
|
||||
}
|
||||
|
||||
public async Task<bool> SaveAsync()
|
||||
{
|
||||
return await _context.SaveChangesAsync() > 0;
|
||||
}
|
||||
|
||||
public async Task BeginTransactionAsync()
|
||||
{
|
||||
if (_transaction is not null)
|
||||
throw new InvalidOperationException("A transaction has already been started.");
|
||||
_transaction = await _context.Database.BeginTransactionAsync();
|
||||
}
|
||||
|
||||
public async Task CommitAsync()
|
||||
{
|
||||
if (_transaction is null)
|
||||
throw new InvalidOperationException("A transaction has not been started.");
|
||||
|
||||
try
|
||||
{
|
||||
await _transaction.CommitAsync();
|
||||
}
|
||||
finally
|
||||
{
|
||||
await _transaction.DisposeAsync();
|
||||
_transaction = null;
|
||||
}
|
||||
}
|
||||
|
||||
private bool disposed = false;
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!disposed)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_transaction?.Dispose(); // Dispose transaction if it exists
|
||||
_context.Dispose();
|
||||
}
|
||||
disposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
16
Database/UnitOfWork/IUnitOfWork.cs
Normal file
16
Database/UnitOfWork/IUnitOfWork.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
|
||||
namespace GamificationService.Database.UnitOfWork;
|
||||
|
||||
public interface IUnitOfWork
|
||||
{
|
||||
#region Repositories
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
bool SaveChanges();
|
||||
Task<bool> SaveChangesAsync();
|
||||
Task BeginTransactionAsync();
|
||||
Task CommitTransactionAsync();
|
||||
Task RollbackTransactionAsync();
|
||||
#endregion
|
||||
}
|
||||
68
Database/UnitOfWork/UnitOfWork.cs
Normal file
68
Database/UnitOfWork/UnitOfWork.cs
Normal file
@@ -0,0 +1,68 @@
|
||||
using Microsoft.EntityFrameworkCore.Storage;
|
||||
|
||||
namespace GamificationService.Database.UnitOfWork;
|
||||
|
||||
public class UnitOfWork : IUnitOfWork
|
||||
{
|
||||
#region Fields
|
||||
private readonly ApplicationContext _context;
|
||||
private IDbContextTransaction? _transaction;
|
||||
|
||||
#endregion
|
||||
|
||||
public UnitOfWork(ApplicationContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
#region Repositories
|
||||
#endregion
|
||||
|
||||
#region Save Methods
|
||||
public bool SaveChanges() => _context.SaveChanges() > 0;
|
||||
|
||||
public async Task<bool> SaveChangesAsync() => (await _context.SaveChangesAsync()) > 0;
|
||||
#endregion
|
||||
|
||||
#region Transactions
|
||||
public async Task BeginTransactionAsync()
|
||||
{
|
||||
if (_transaction is not null)
|
||||
throw new InvalidOperationException("A transaction has already been started.");
|
||||
|
||||
_transaction = await _context.Database.BeginTransactionAsync();
|
||||
}
|
||||
|
||||
public async Task CommitTransactionAsync()
|
||||
{
|
||||
if (_transaction is null)
|
||||
throw new InvalidOperationException("A transaction has not been started.");
|
||||
|
||||
try
|
||||
{
|
||||
await _transaction.CommitAsync();
|
||||
}
|
||||
catch
|
||||
{
|
||||
await RollbackTransactionAsync();
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
await _transaction.DisposeAsync();
|
||||
_transaction = null;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task RollbackTransactionAsync()
|
||||
{
|
||||
if (_transaction is null)
|
||||
return;
|
||||
|
||||
await _transaction.RollbackAsync();
|
||||
await _transaction.DisposeAsync();
|
||||
_transaction = null;
|
||||
}
|
||||
#endregion
|
||||
|
||||
}
|
||||
12
Dockerfile
12
Dockerfile
@@ -7,17 +7,17 @@ EXPOSE 8081
|
||||
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
|
||||
ARG BUILD_CONFIGURATION=Release
|
||||
WORKDIR /src
|
||||
COPY ["PurpleHackBackend/PurpleHackBackend.csproj", "PurpleHackBackend/"]
|
||||
RUN dotnet restore "PurpleHackBackend/PurpleHackBackend.csproj"
|
||||
COPY ["GamificationService.csproj", "GamificationService/"]
|
||||
RUN dotnet restore "GamificationService/GamificationService.csproj"
|
||||
COPY . .
|
||||
WORKDIR "/src/PurpleHackBackend"
|
||||
RUN dotnet build "PurpleHackBackend.csproj" -c $BUILD_CONFIGURATION -o /app/build
|
||||
WORKDIR "/src/GamificationService"
|
||||
RUN dotnet build -c $BUILD_CONFIGURATION -o /app/build
|
||||
|
||||
FROM build AS publish
|
||||
ARG BUILD_CONFIGURATION=Release
|
||||
RUN dotnet publish "PurpleHackBackend.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
|
||||
RUN dotnet publish "GamificationService.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
|
||||
|
||||
FROM base AS final
|
||||
WORKDIR /app
|
||||
COPY --from=publish /app/publish .
|
||||
ENTRYPOINT ["dotnet", "PurpleHackBackend.dll"]
|
||||
ENTRYPOINT ["dotnet", "GamificationService.dll"]
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using GamificationService.Database;
|
||||
using GamificationService.Database.Repositories;
|
||||
using GamificationService.Database.GenericRepository;
|
||||
using GamificationService.Database.UnitOfWork;
|
||||
using GamificationService.Loggging;
|
||||
using GamificationService.Mapper;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
|
||||
Reference in New Issue
Block a user