天天看點

FreeSql.Repository (四)工作單元

歡迎來到《FreeSql.Repository 倉儲模式》系列文檔,本系列文檔專注介紹 【倉儲+工作單元】 的使用方式。完整文檔請前往 wiki 中心:https://github.com/dotnetcore/FreeSql/wiki

UnitOfWork 可将多個倉儲放在一個單元管理執行,最終通用 Commit 執行所有操作,内部采用了資料庫事務;

羅裡吧嗦一堆,簡單點了解:把它看成事務

工作單元定義

public interface IUnitOfWork : IDisposable
{
    /// <summary>
    /// 開啟事務,或者傳回已開啟的事務
    /// </summary>
    /// <param name="isCreate">若未開啟事務,則開啟</param>
    /// <returns></returns>
    DbTransaction GetOrBeginTransaction(bool isCreate = true);
    IsolationLevel? IsolationLevel { get; set; }
    void Commit();
    void Rollback();
}
           

除上述的定義,我們增加了 IFreeSql Orm 屬性通路原始用法,并且保持在一個事務單元執行。

如何使用

工作單元建議使用 IoC 管理生命周期,否則用起來那叫一個麻煩:

using (var uow = fsql.CreateUnitOfWork())
{
    var songRepo = fsql.GetRepository<Song>();
    var userRepo = fsql.GetRepository<User>();
    songRepo.UnitOfWork = uow; //手工綁定工作單元
    userRepo.UnitOfWork = uow;

    songRepo.Insert(new Song());
    userRepo.Update(...);

    uow.Orm.Insert(new Song()).ExecuteAffrows();
    //注意:uow.Orm 和 fsql 都是 IFreeSql
    //uow.Orm CRUD 與 uow 是一個事務(了解為臨時 IFreeSql)
    //fsql CRUD 與 uow 不在一個事務

    uow.Commit();
}
           

依賴注入

以 webapi 類型項目為例,如果注入 IUnitOfWork,一次請求隻能開啟一個工作單元事務。是以我們引入工作單元管理器(UnitOfWorkManager)的概念,負責管理請求内的一組工作單元。

本章節内容有點繁瑣,不過它是一勞永逸的,建議耐着性子看完,并且使用起來。從此不再為事務的用法煩惱掉發……

UnitOfWorkManager 支援六種傳播方式(propagation),意味着跨方法的事務非常友善,并且支援同步異步:

  • Requierd:如果目前沒有事務,就建立一個事務,如果已存在一個事務中,加入到這個事務中,預設的選擇。
  • Supports:支援目前事務,如果沒有目前事務,就以非事務方法執行。
  • Mandatory:使用目前事務,如果沒有目前事務,就抛出異常。
  • NotSupported:以非事務方式執行操作,如果目前存在事務,就把目前事務挂起。
  • Never:以非事務方式執行操作,如果目前事務存在則抛出異常。
  • Nested:以嵌套事務方式執行。
UnitOfWorkManager 成員 說明
IUnitOfWork Current 傳回目前的工作單元
void Binding(repository) 将倉儲的事務交給它管理
IUnitOfWork Begin(propagation, isolationLevel) 建立工作單元

第一步:配置 Startup.cs 注入

public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton<IFreeSql>(fsql);
    services.AddScoped<UnitOfWorkManager>();
    services.AddFreeRepository(null, typeof(Startup).Assembly);
}
           

第二步:定義事務特性

[AttributeUsage(AttributeTargets.Method)]
public class TransactionalAttribute : Attribute
{
    public Propagation Propagation { get; set; } = Propagation.Requierd;
    public IsolationLevel? IsolationLevel { get; set; }
}
           

第三步:引入動态代理庫

在 Before 從容器中擷取 UnitOfWorkManager,調用它的 var uow = Begin(attr.Propagation, attr.IsolationLevel) 方法

在 After 調用 Before 中的 uow.Commit 或者 Rollback 方法,最後調用 uow.Dispose

第四步:在 Controller 或者 Service 中使用事務特性

public class SongService
{
    BaseRepository<Song> _repoSong;
    BaseRepository<Detail> _repoDetail;
    SongRepository _repoSong2;

    public SongService(
        BaseRepository<Song> repoSong,
        BaseRepository<Detail> repoDetail,
        SongRepository repoSong2)
    {
        _repoSong = repoSong;
        _repoDetail = repoDetail;
        _repoSong2 = repoSong2;
    }

    [Transactional]
    public virtual void Test1()
    {
        //這裡 _repoSong、_repoDetail、_repoSong2 所有操作都是一個工作單元
        this.Test2();
    }

    [Transactional(Propagation = Propagation.Nested)]
    public virtual void Test2() //嵌套事務,新的(不使用 Test1 的事務)
    {
        //這裡 _repoSong、_repoDetail、_repoSong2 所有操作都是一個工作單元
    }
}
           

是不是進方法就開事務呢?

不一定是真實事務,有可能是虛的,就是一個假的 unitofwork(不帶事務)

也有可能是延用上一次的事務

也有可能是新開事務,具體要看 Propagation 傳播模式

示範項目:https://github.com/dotnetcore/FreeSql/tree/master/Examples/aspnetcore_transaction

實戰項目:https://github.com/zhontai

系列文章導航

  • (一)什麼是倉儲
  • (二)如何使用倉儲
  • (三)實體特性
  • (四)工作單元
  • (五)狀态管理
  • (六)導航屬性
  • (七)多表查詢
  • (八)級聯加載
  • (九)級聯儲存
  • (十)動态實體類型
  • (十一)分表
  • (十二)如何擴充

繼續閱讀