diff --git a/Unit-of-Work.md b/Unit-of-Work.md new file mode 100644 index 0000000..b1b4c55 --- /dev/null +++ b/Unit-of-Work.md @@ -0,0 +1,127 @@ +[中文](%e5%b7%a5%e4%bd%9c%e5%8d%95%e5%85%83) | **English** + +UnitOfWork 可将多个仓储放在一个单元管理执行,最终通用 Commit 执行所有操作,内部采用了数据库事务; + +```csharp +static IFreeSql fsql = new FreeSql.FreeSqlBuilder() + .UseConnectionString(FreeSql.DataType.MySql, connectionString) + .UseAutoSyncStructure(true) //自动同步实体结构到数据库 + .Build(); //请务必定义成 Singleton 单例模式 +``` + +## 如何使用 + +```csharp +using (var uow = fsql.CreateUnitOfWork()) +{ + var songRepo = fsql.GetRepository(); + var userRepo = fsql.GetRepository(); + 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(); +} +``` + +参考:[在 asp.net core 中使用 TransactionalAttribute + UnitOfWorkManager 实现多种事务传播](https://github.com/dotnetcore/FreeSql/issues/289) + +## 接口定义 + +uow.GetOrBeginTransaction() 方法可获取事务对象。 + +```csharp +public interface IUnitOfWork : IDisposable +{ + /// + /// 该对象 Select/Delete/Insert/Update/InsertOrUpdate 与工作单元事务保持一致,可省略传递 WithTransaction + /// + IFreeSql Orm { get; } + + /// + /// 开启事务,或者返回已开启的事务 + /// + /// 若未开启事务,则开启 + /// + DbTransaction GetOrBeginTransaction(bool isCreate = true); + + IsolationLevel? IsolationLevel { get; set; } + + void Commit(); + + void Rollback(); + + /// + /// 工作单元内的实体变化跟踪 + /// + DbContext.EntityChangeReport EntityChangeReport { get; } +} +``` + +## 实体变化事件 + +全局设置: + +```csharp +fsql.SetDbContextOptions(opt => { + opt.OnEntityChange = report => { + Console.WriteLine(report); + }; +}); +``` + +单独设置: + +```csharp +var uow = fsql.CreateUnitOfWork(); +uow.OnEntityChange = report => { + Console.WriteLine(report); +}; +``` + +参数 report 是一个 List 集合,集合元素的类型定义如下: + +```csharp +public class ChangeInfo { + public object Object { get; set; } + public EntityChangeType Type { get; set; } + /// + /// Type = Update 的时候,获取更新之前的对象 + /// + public object BeforeObject { get; set; } +} +public enum EntityChangeType { Insert, Update, Delete, SqlRaw } +``` + +| 变化类型 | 说明 | +| -- | -- | +| Insert | 实体对象被插入 | +| Update | 实体对象被更新 | +| Delete | 实体对象被删除 | +| SqlRaw | 执行了SQL语句 | + +SqlRaw 目前有两处地方比较特殊: +- 多对多联级更新导航属性的时候,对中间表的全部删除操作; +- 通用仓储类 BaseRepository 有一个 Delete 方法,参数为表达式,而并非实体; +```csharp +int Delete(Expression> predicate); +``` + +DbContext.SaveChanges,或者 Repository 对实体的 Insert/Update/Delete,或者 UnitOfWork.Commit 操作都会最多触发一次该事件。 + +## 参考资料 + +- [《租户》](%e7%a7%9f%e6%88%b7) +- [《读写分离》](%e8%af%bb%e5%86%99%e5%88%86%e7%a6%bb) +- [《分表、分库》](%e5%88%86%e8%a1%a8%e5%88%86%e5%ba%93) +- [《仓储层Repository》](Repository) +- [《过滤器、全局过滤器》](%e8%bf%87%e6%bb%a4%e5%99%a8) +- [《AOP》](AOP) +- [《DbContext》](DbContext) \ No newline at end of file