feat: Generic Repository 및 UnitOfWork 패턴 구현 (#18)
- IRepository<T> 구현체: CRUD, 페이징, 조건 검색 지원 - IUnitOfWork 구현체: 트랜잭션 관리 (Begin/Commit/Rollback) - Program.cs에 DI 등록 (AddScoped) Closes #18
This commit is contained in:
parent
3293c38360
commit
787190f512
|
|
@ -1,6 +1,9 @@
|
|||
using Microsoft.EntityFrameworkCore;
|
||||
using SPMS.API.Middlewares;
|
||||
using SPMS.Domain.Interfaces;
|
||||
using SPMS.Infrastructure;
|
||||
using SPMS.Infrastructure.Persistence;
|
||||
using SPMS.Infrastructure.Persistence.Repositories;
|
||||
|
||||
|
||||
var builder = WebApplication.CreateBuilder(new WebApplicationOptions
|
||||
|
|
@ -17,6 +20,8 @@ var connectionString = builder.Configuration.GetConnectionString("DefaultConnect
|
|||
builder.Services.AddDbContext<AppDbContext>(options =>
|
||||
options.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString)));
|
||||
|
||||
builder.Services.AddScoped(typeof(IRepository<>), typeof(Repository<>));
|
||||
builder.Services.AddScoped<IUnitOfWork, UnitOfWork>();
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
|
|
|
|||
70
SPMS.Infrastructure/Persistence/Repositories/Repository.cs
Normal file
70
SPMS.Infrastructure/Persistence/Repositories/Repository.cs
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
using System.Linq.Expressions;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SPMS.Domain.Entities;
|
||||
using SPMS.Domain.Interfaces;
|
||||
|
||||
namespace SPMS.Infrastructure.Persistence.Repositories;
|
||||
|
||||
public class Repository<T> : IRepository<T> where T : BaseEntity
|
||||
{
|
||||
protected readonly AppDbContext _context;
|
||||
protected readonly DbSet<T> _dbSet;
|
||||
|
||||
public Repository(AppDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
_dbSet = context.Set<T>();
|
||||
}
|
||||
|
||||
public async Task<T?> GetByIdAsync(long id)
|
||||
=> await _dbSet.FindAsync(id);
|
||||
|
||||
public async Task<IReadOnlyList<T>> GetAllAsync()
|
||||
=> await _dbSet.ToListAsync();
|
||||
|
||||
public async Task<IReadOnlyList<T>> FindAsync(Expression<Func<T, bool>> predicate)
|
||||
=> await _dbSet.Where(predicate).ToListAsync();
|
||||
|
||||
public async Task<(IReadOnlyList<T> Items, int TotalCount)> GetPagedAsync(
|
||||
int page, int size,
|
||||
Expression<Func<T, bool>>? predicate = null,
|
||||
Expression<Func<T, object>>? orderBy = null,
|
||||
bool descending = true)
|
||||
{
|
||||
IQueryable<T> query = _dbSet;
|
||||
|
||||
if (predicate is not null)
|
||||
query = query.Where(predicate);
|
||||
|
||||
var totalCount = await query.CountAsync();
|
||||
|
||||
if (orderBy is not null)
|
||||
query = descending ? query.OrderByDescending(orderBy) : query.OrderBy(orderBy);
|
||||
else
|
||||
query = query.OrderByDescending(e => e.Id);
|
||||
|
||||
var items = await query
|
||||
.Skip((page - 1) * size)
|
||||
.Take(size)
|
||||
.ToListAsync();
|
||||
|
||||
return (items, totalCount);
|
||||
}
|
||||
|
||||
public async Task<int> CountAsync(Expression<Func<T, bool>>? predicate = null)
|
||||
=> predicate is null
|
||||
? await _dbSet.CountAsync()
|
||||
: await _dbSet.CountAsync(predicate);
|
||||
|
||||
public async Task<bool> ExistsAsync(Expression<Func<T, bool>> predicate)
|
||||
=> await _dbSet.AnyAsync(predicate);
|
||||
|
||||
public async Task AddAsync(T entity)
|
||||
=> await _dbSet.AddAsync(entity);
|
||||
|
||||
public void Update(T entity)
|
||||
=> _dbSet.Update(entity);
|
||||
|
||||
public void Delete(T entity)
|
||||
=> _dbSet.Remove(entity);
|
||||
}
|
||||
50
SPMS.Infrastructure/Persistence/UnitOfWork.cs
Normal file
50
SPMS.Infrastructure/Persistence/UnitOfWork.cs
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
using Microsoft.EntityFrameworkCore.Storage;
|
||||
using SPMS.Domain.Interfaces;
|
||||
|
||||
namespace SPMS.Infrastructure.Persistence;
|
||||
|
||||
public class UnitOfWork : IUnitOfWork
|
||||
{
|
||||
private readonly AppDbContext _context;
|
||||
private IDbContextTransaction? _transaction;
|
||||
|
||||
public UnitOfWork(AppDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
|
||||
=> await _context.SaveChangesAsync(cancellationToken);
|
||||
|
||||
public async Task<IDisposable> BeginTransactionAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
_transaction = await _context.Database.BeginTransactionAsync(cancellationToken);
|
||||
return _transaction;
|
||||
}
|
||||
|
||||
public async Task CommitTransactionAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (_transaction is null)
|
||||
throw new InvalidOperationException("Transaction has not been started.");
|
||||
|
||||
await _transaction.CommitAsync(cancellationToken);
|
||||
await _transaction.DisposeAsync();
|
||||
_transaction = null;
|
||||
}
|
||||
|
||||
public async Task RollbackTransactionAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (_transaction is null)
|
||||
throw new InvalidOperationException("Transaction has not been started.");
|
||||
|
||||
await _transaction.RollbackAsync(cancellationToken);
|
||||
await _transaction.DisposeAsync();
|
||||
_transaction = null;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_transaction?.Dispose();
|
||||
_context.Dispose();
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user