如何使用仓储 - 工作单元
前言
本文主要介绍事务与工作单元,仓储的基础方法使用请查看 如何使用仓储(一)-基础功能
。 22.Adnc.Infr.EfCore
是仓储的实现工程,集成了EFcore作为仓储的实现。
EFCore的事务
EFCore的事务分为以下三种
- SaveChanges
- DbContextTransaction
- TransactionScope
EFCore默认情况下,是开启了SaveChanges事务的。然而基于以下原因Adnc关闭了SaveChanges自动事务。
- UpdateRangeAsync,DeleteRangeAsync 批量删除,批量更新方法不是走EfCore的原生方法,不受SaveChanges事务控制。
- Cap的事务也不受SaveChanges事务控制
- 最后一点就是与读写分离相关,请参考
如何实现读写分离
public class AdncDbContext : DbContext
{
public AdncDbContext()
{
Database.AutoTransactionsEnabled = false;
}
}
在关闭SaveChanges自动事务后,Adnc通过DbContextTransaction统一控制事务。如果你的业务逻辑调用增/删/改的方法>=2次,就需要显示声明拦截器来开启事务控制。
主从表插入、主从表更新、批量新增、批量修改、批量删除操作都可以通过一个增/删/改的方法实现,并不需要显示开启事务。
22.Adnc.Infr.EfCore
实现了03.Adnc.Core.Shared
的IUnitOfWork
接口,并且在AdncInfrEfCoreModule.cs
文件中已经注册。工作单元拦截器在03.Adnc.Core.Shared
工程的 Interceptors
目录中,都已经实现好。我们只需要在使用的地方注入拦截器,然后显示声明。
如何使用
注册
在Application(AdncxxxApplicationModule.cs
)或者Core(AdncxxxCoreModule.cs
)工程注册拦截器。
如果采用经典三层开发模式,在Application或者Core层注册都可以。 如果采用DDD开发模式,则只能在Application层注册。
在经典三层开发模式中,Adnc是在Core层注册的,主要还是基于灵活控制读写分离的目的。如果在Core层使用工作单元来控制事务,意味着你需要多写一些一点代码。对于经典三层开发模式来说,在Application或者Core层注册,都没有问题,取决于你自己的考虑。
public class AdncCusCoreModule : Module
{
/// <summary>
/// Autofac注册
/// </summary>
/// <param name="builder"></param>
protected override void Load(ContainerBuilder builder)
{
//注册其它组件
// todo
//注册事务拦截器
builder.RegisterType<UowInterceptor>()
.InstancePerLifetimeScope();
builder.RegisterType<UowAsyncInterceptor>()
.InstancePerLifetimeScope();
//注册Core服务
builder.RegisterAssemblyTypes(this.ThisAssembly)
.Where(t => t.IsAssignableTo<ICoreService>())
.AsSelf()
.InstancePerLifetimeScope()
.EnableClassInterceptors()
.InterceptedBy(typeof(UowInterceptor));
}
}
显示声明
如果方法中需要用到Cap发布事件,需要使用[UnitOfWork(SharedToCap = true)]
声明。
[UnitOfWork]
public virtual async Task ProcessRechargingAsync(long transactionLogId, long customerId, decimal amount)
{
var transLog = await _cusTransactionLogRepo.FindAsync(transactionLogId, noTracking: false);
if (transLog == null || transLog.ExchageStatus != ExchageStatusEnum.Processing)
return;
var finance = await _cusFinaceRepo.FindAsync(customerId, noTracking: false);
var originalBalance = finance.Balance;
var newBalance = originalBalance + amount;
finance.Balance = newBalance;
await _cusFinaceRepo.UpdateAsync(finance);
transLog.ExchageStatus = ExchageStatusEnum.Finished;
transLog.ChangingAmount = originalBalance;
transLog.ChangedAmount = newBalance;
await _cusTransactionLogRepo.UpdateAsync(transLog);
}
[UnitOfWork(SharedToCap = true)]
public virtual async Task RechargeAsync(CustomerTransactionLog cusTransactionLog, CancellationToken cancellationToken = default)
{
await _cusTransactionLogRepo.InsertAsync(cusTransactionLog);
//发布充值事件
var eventId = IdGenerater.GetNextId(IdGenerater.DatacenterId, IdGenerater.WorkerId);
var eventData = new CustomerRechargedEvent.EventData() { CustomerId = cusTransactionLog.CustomerId, TransactionLogId = cusTransactionLog.Id, Amount = cusTransactionLog.Amount };
var eventSource = System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName;
await _eventPublisher.PublishAsync(new CustomerRechargedEvent(eventId, eventData, eventSource));
}