iT邦幫忙

2023 iThome 鐵人賽

DAY 10
0
SideProject30

我想自己刻部落格系列 第 10

關於三層式架構

  • 分享至 

  • xImage
  •  

安裝完 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

參考資料1
參考資料2


上一篇
使用 EF Core 反向工程產生資料庫物件
下一篇
建立各資料表的 Repository 與註冊 DbContext
系列文
我想自己刻部落格31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言