歡迎來到《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