安裝完 EF Core 之後,我們的專案就擁有對資料庫做 CRUD 的能力。
通常專案初期會以職責劃分分成多層結構來開發,使每段程式碼的職責分開
使專案隨著開發的過程中逐漸成長時,比較好維護。
(出 bug 時能快速找到兇手,做好責任歸屬)
所以這專案會這樣區分
Controller -> Service -> Repository -> DB
大部分的邏輯判斷處理都集中在 Service
Repository 與資料庫對接負責處理資料的 CRUD
Controller 就負責資料的接收與傳送,個人習慣會盡量讓 Controller 的工作越少越好。
這樣的規劃剛好符合三層式架構。
表現層 -> 業務邏輯層(Business Layer) -> 資料存取層(Data Access Layer)
Repository 層,剛好也是指設計模式中的儲存庫模式(Repository Patten)。指將資料的存取邏輯獨立出來,實現單一職則原則。
而 Service 層只負責邏輯判斷,如果想做單元測試,別可以獨立於資料庫之外,指對 Service 層做測試。
在 Repository 中,一個資料庫 Table 會有一個專門的 Repository 來處理。
此外也有跨 Table 統合處理的需求。使用 Repository 模式時,同時也會搭配 Unit of Work 模式來統一管理。
可以想像成 Unit of Work 是 DB,Repository 只是 Table
所以我們會以 Repository 與 Unit of Work 來操作 EF Core 幫我們處理資料。
初步的程式碼如下:
IUnitOfWork.cs
public interface IUnitOfWork : IDisposable
{
DbContext Context { get; set; }
void Save();
}
IRepository.cs
public interface IRepository<T> where T : class
{
IUnitOfWork UnitOfWork { get; set; }
Task<T?> GetAsync(Expression<Func<T, bool>> filter);
IQueryable<T>? GetAll();
IQueryable<T>? Query(Expression<Func<T, bool>> filter);
Task CreateAsync(T entity);
void Delete(T entity);
}
UnitOfWork.cs
public class UnitOfWork : IUnitOfWork
{
public DbContext Context { get; set; }
public UnitOfWork(DbContext dbContext)
{
Context = dbContext;
}
public void Dispose()
{
Context.Dispose();
GC.SuppressFinalize(this);
}
public void Save()
{
Context.SaveChanges();
}
}
Repository.cs
public class Repository<T> : IRepository<T> where T : class
{
public IUnitOfWork UnitOfWork { get; set; }
private DbSet<T>? _dbSet;
private DbSet<T> DbSet => _dbSet ??= UnitOfWork.Context.Set<T>();
public Repository(IUnitOfWork unitOfWork)
{
UnitOfWork = unitOfWork;
}
public async Task CreateAsync(T entity)
{
await DbSet.AddAsync(entity);
}
public void Delete(T entity)
{
DbSet.Remove(entity);
}
public async Task<T?> GetAsync(Expression<Func<T, bool>> filter)
{
return await DbSet.SingleOrDefaultAsync(filter);
}
public IQueryable<T>? GetAll()
{
return DbSet;
}
public IQueryable<T>? Query(Expression<Func<T, bool>> filter)
{
return GetAll().Where(filter);
}
}
程式碼可以看我的 GitHub