feat: Generic Repository 및 UnitOfWork 패턴 구현 (#18) #19
|
|
@ -1,6 +1,9 @@
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using SPMS.API.Middlewares;
|
using SPMS.API.Middlewares;
|
||||||
|
using SPMS.Domain.Interfaces;
|
||||||
using SPMS.Infrastructure;
|
using SPMS.Infrastructure;
|
||||||
|
using SPMS.Infrastructure.Persistence;
|
||||||
|
using SPMS.Infrastructure.Persistence.Repositories;
|
||||||
|
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(new WebApplicationOptions
|
var builder = WebApplication.CreateBuilder(new WebApplicationOptions
|
||||||
|
|
@ -17,6 +20,8 @@ var connectionString = builder.Configuration.GetConnectionString("DefaultConnect
|
||||||
builder.Services.AddDbContext<AppDbContext>(options =>
|
builder.Services.AddDbContext<AppDbContext>(options =>
|
||||||
options.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString)));
|
options.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString)));
|
||||||
|
|
||||||
|
builder.Services.AddScoped(typeof(IRepository<>), typeof(Repository<>));
|
||||||
|
builder.Services.AddScoped<IUnitOfWork, UnitOfWork>();
|
||||||
|
|
||||||
var app = builder.Build();
|
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