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

系列文章導航


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM