UnitOfWorkManager 正是干这件事的。避免了每次对数据操作都要现获得 Session 实例来启动事务/提交/回滚事务还有繁琐的Try/Catch操作。这些也是 AOP(面向切面编程)机制很好的应用。一方面使开发业务逻辑更清晰、专业分工更加容易进行。另一方面就是应用 AOP 隔离降低了程序的耦合性使我们可以在不同的应用中将各个切面结合起来使用大大提高了代码重用度。
使用前准备第一步:配置 Startup.cs 注入
//Startup.cs public void ConfigureServices(IServiceCollection services) { services.AddSingleton<IFreeSql>(fsql); services.AddScoped<UnitOfWorkManager>(); services.AddFreeRepository(null, typeof(Startup).Assembly); } UnitOfWorkManager 成员 说明IUnitOfWork Current 返回当前的工作单元
void Binding(repository) 将仓储的事务交给它管理
IUnitOfWork Begin(propagation, isolationLevel) 创建工作单元
使用前准备第二步:定义事务特性
[AttributeUsage(AttributeTargets.Method)] public class TransactionalAttribute : Attribute { /// <summary> /// 事务传播方式 /// </summary> public Propagation Propagation { get; set; } = Propagation.Requierd; /// <summary> /// 事务隔离级别 /// </summary> public IsolationLevel? IsolationLevel { get; set; } }使用前准备第三步:引入动态代理库
在 Before 从容器中获取 UnitOfWorkManager,调用它的 var uow = uowManager.Begin(attr.Propagation, attr.IsolationLevel) 方法
在 After 调用 Before 中的 uow.Commit 或者 Rollback 方法,最后调用 uow.Dispose
自问自答:是不是进方法就开事务呢?
不一定是真实事务,有可能是虚的,就是一个假的 unitofwork(不带事务),也有可能是延用上一次的事务,也有可能是新开事务,具体要看传播模式。
二、IFreeSql.InsertOrUpdate 批量插入或更新IFreeSql 定义了 InsertOrUpdate 方法实现批量插入或更新的功能,利用的是数据库特性进行保存,执行时根据数据库自动适配:
Database FeaturesMySql on duplicate key update
PostgreSQL on conflict do update
SqlServer merge into
Oracle merge into
Sqlite replace into
Dameng merge into
fsql.InsertOrUpdate<T>() .SetSource(items) //需要操作的数据 .ExecuteAffrows();
由于我们前面定义 fsql 变量的类型是 MySql,所以执行的语句大概是这样的:
INSERT INTO `T`(`id`, `name`) VALUES(1, '001'), (2, '002'), (3, '003'), (4, '004') ON DUPLICATE KEY UPDATE `name` = VALUES(`name`)当实体类有自增属性时,批量 InsertOrUpdate 最多可被拆成两次执行,内部计算出未设置自增值、和有设置自增值的数据,分别执行 insert into 和 上面讲到的 merge into 两种命令(采用事务执行)。
三、WhereDynamicFilter 动态过滤是否见过这样的高级查询功能,WhereDynamicFilter 在后端可以轻松完成这件事情,前端根据 UI 组装好对应的 json 字符串传给后端就行,如下:
DynamicFilterInfo dyfilter = JsonConvert.DeserializeObject<DynamicFilterInfo>(@" { ""Logic"" : ""Or"", ""Filters"" : [ { ""Field"" : ""Code"", ""Operator"" : ""NotContains"", ""Value"" : ""val1"", ""Filters"" : [ { ""Field"" : ""Name"", ""Operator"" : ""NotStartsWith"", ""Value"" : ""val2"", } ] }, { ""Field"" : ""Parent.Code"", ""Operator"" : ""Eq"", ""Value"" : ""val11"", ""Filters"" : [ { ""Field"" : ""Parent.Name"", ""Operator"" : ""Contains"", ""Value"" : ""val22"", } ] } ] } "); fsql.Select<VM_District_Parent>().WhereDynamicFilter(dyfilter).ToList(); //SELECT a.""Code"", a.""Name"", a.""ParentCode"", a__Parent.""Code"" as4, a__Parent.""Name"" as5, a__Parent.""ParentCode"" as6 //FROM ""D_District"" a //LEFT JOIN ""D_District"" a__Parent ON a__Parent.""Code"" = a.""ParentCode"" //WHERE (not((a.""Code"") LIKE '%val1%') AND not((a.""Name"") LIKE 'val2%') OR a__Parent.""Code"" = 'val11' AND (a__Parent.""Name"") LIKE '%val22%')