.NetCore基於SqlSugar和Aop的工作單元模式(UnitOfWork)實現 Unit Of Work 是什麼 Unit Of Work模式,即工作單元,它是一種數據訪問模式。它是用來維護一個由已經被業務修改(如增加、刪除和更新等)的業務對象組成的列表。它負責協調這些業務對象的持久化工作及 ...
.NetCore基於SqlSugar和Aop的工作單元模式(UnitOfWork)實現
Unit Of Work 是什麼
Unit Of Work模式,即工作單元,它是一種數據訪問模式。它是用來維護一個由已經被業務修改(如增加、刪除和更新等)的業務對象組成的列表。它負責協調這些業務對象的持久化工作及併發問題。通過資料庫事務Unit Of Work模式會記錄所有對象模型修改過的信息,在提交的時候,一次性修改,並把結果同步到資料庫。 這個過程通常被封裝在事務中。所以在採用Unit Of Work模式好處就在於能夠確保數據的完整性,如果在持有一系列業務對象(同屬於一個事務)的過程中出現問題,就可以將所有的修改回滾,以確保數據始終處於有效狀態,不會出現臟數據。
用一句話概括就是:它讓一個介面內的所有增刪改操作都在一個事務中,要麼同時成功提交,要麼同時失敗回滾。
代碼實現
創建工作單元依賴介面:
/// <summary>
/// 工作單元依賴介面
/// </summary>
public interface IUnitOfWork
{
/// <summary>
/// 開啟工作單元處理
/// </summary>
/// <param name="context"></param>
/// <param name="unitOfWork"></param>
void BeginTransaction(ActionExecutingContext context);
/// <summary>
/// 提交工作單元處理
/// </summary>
/// <param name="resultContext"></param>
/// <param name="unitOfWork"></param>
void CommitTransaction(ActionExecutedContext resultContext);
/// <summary>
/// 回滾工作單元處理
/// </summary>
/// <param name="resultContext"></param>
/// <param name="unitOfWork"></param>
void RollbackTransaction(ActionExecutedContext resultContext);
/// <summary>
/// 執行完畢(無論成功失敗)
/// </summary>
/// <param name="context"></param>
/// <param name="resultContext"></param>
void OnCompleted(ActionExecutingContext context, ActionExecutedContext resultContext);
}
繼承介面並實現:
public class SqlSugarUnitOfWork : IUnitOfWork
{
private readonly ISqlSugarClient _sqlSugarClient;
public SqlSugarUnitOfWork(ISqlSugarClient sqlSugarClient)
{
this._sqlSugarClient = sqlSugarClient;
}
public void BeginTransaction(ActionExecutingContext context)
{
_sqlSugarClient.AsTenant().BeginTran();
}
public void CommitTransaction(ActionExecutedContext resultContext)
{
_sqlSugarClient.AsTenant().CommitTran();
}
public void OnCompleted(ActionExecutingContext context, ActionExecutedContext resultContext)
{
_sqlSugarClient.Dispose();
}
public void RollbackTransaction(ActionExecutedContext resultContext)
{
_sqlSugarClient.AsTenant().RollbackTran();
}
}
註入到容器中:
services.AddTransient<IUnitOfWork, SqlSugarUnitOfWork>(); // 註冊工作單元到容器
此時,工作單元已經添加到容器中,為了更方便的使用,我們可以使用AspNetCore框架自帶的過濾器實現AOP的方式來使用工作單元。
首先需要創建工作單元特性(特性是為了方便配置哪些介面需要啟用工作單元):
/// <summary>
/// 工作單元配置特性
/// </summary>
[AttributeUsage(AttributeTargets.Method)]
public class UnitOfWorkAttribute : Attribute {}
然後創建過濾器:
/// <summary>
/// 工作單元Action過濾器
/// </summary>
public class UnitOfWorkFilter : IAsyncActionFilter, IOrderedFilter
{
private readonly ILogger<UnitOfWorkFilter> _logger;
public UnitOfWorkFilter(ILogger<UnitOfWorkFilter> logger)
{
this._logger = logger;
}
/// <summary>
/// 過濾器排序
/// </summary>
internal const int FilterOrder = 999;
/// <summary>
/// 排序屬性
/// </summary>
public int Order => FilterOrder;
/// <summary>
/// 攔截請求
/// </summary>
/// <param name="context">動作方法上下文</param>
/// <param name="next">中間件委托</param>
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
// 獲取動作方法描述器
var actionDescriptor = context.ActionDescriptor as ControllerActionDescriptor;
var method = actionDescriptor.MethodInfo;
// 獲取請求上下文
var httpContext = context.HttpContext;
// 如果沒有定義工作單元過濾器,則跳過
if (!method.IsDefined(typeof(UnitOfWorkAttribute), true))
{
// 調用方法
_ = await next();
return;
}
// 列印工作單元開始消息
_logger.LogInformation($@"{nameof(UnitOfWorkFilter)} Beginning");
// 解析工作單元服務
var _unitOfWork = httpContext.RequestServices.GetRequiredService<IUnitOfWork>();
// 調用開啟事務方法
_unitOfWork.BeginTransaction(context);
// 獲取執行 Action 結果
var resultContext = await next();
if (resultContext == null || resultContext.Exception == null)
{
// 調用提交事務方法
_unitOfWork.CommitTransaction(resultContext);
}
else
{
// 調用回滾事務方法
_unitOfWork.RollbackTransaction(resultContext);
}
// 調用執行完畢方法
_unitOfWork.OnCompleted(context, resultContext);
// 列印工作單元結束消息
_logger.LogInformation($@"{nameof(UnitOfWorkFilter)} Ending");
}
}
需要將過濾器添加到通信管道中才會起作用:
services.AddControllers(options =>
{
// 添加 工作單元過濾器
options.Filters.Add<UnitOfWorkFilter>();
});
到這一步,工作單元已經準備好了。
接下來是調用方式,很簡單,只要在你想啟用工作單元的介面上(也就是Controller中的Action),加上工作單元的特性就可以了,像這樣:
[Route("test")]
public class TestController : ControllerBase
{
private readonly IBaseRepository<Student> _studentRepository;
private readonly IBaseRepository<Teacher> _teacherRepository;
public TestController(IBaseRepository<Student> studentRepository,
IBaseRepository<Teacher> teacherRepository)
{
this._studentRepository = studentRepository;
this._teacherRepository = teacherRepository;
}
[HttpGet, UnitOfWork]
public async Task<IActionResult> AddTestAsync()
{
await _studentRepository.InsertAsync(new Student
{
Name = "Hello",
Age = 22
});
await _teacherRepository.InsertAsync(new Teacher
{
Name = "World",
Age = 35,
Subject = 1
});
return Ok("Ok");
}
}
由上面的例子可以看到,_studentRepository 和 _teacherRepository 兩個倉儲分別進行了添加操作,但是他們實際是在一個事務中進行的,所以如果出現異常,事務中的所有操作都會回滾,從而達到操作原子性。
為了縮減篇幅,代碼我省略一部分,詳細的示例項目上傳到GitHub:
https://github.com/young-qiang/UnitOfWork.Demo.git