之前在我的文章中,通过实例展示Asp.Net Core 2.0 之旅---AutoFac 仓储泛型的依赖注入,并在结尾处提到了事务的统一管理。
仓储的概念是DDD领域的一部分,仓储封装了获取数据的逻辑,领域对象无需和底层数据库打交道。但是从之前的仓储实现中,每个增删改查都显示的调用了SaveChanges方法,导致每次更改都提交事务。如果我们操作多个仓储对象时,就无法批量提交,无法实现Unit Of Work模式了(UOW模式是,通过事务,一次性提交所以的更改,保证了数据的完整性),要实现UOW,接下来就要对仓储实现的方法就行调整了。
1、定义IUnitOfWork接口,定义两个方法,SaveChanges 同步方法,SaveChangesAsync 异步方法
public interface IUnitOfWork
{
/// <summary>
/// 提交更改
/// </summary>
/// <returns></returns>
int SaveChanges();
/// <summary>
/// 提交更改异步
/// </summary>
/// <returns></returns>
Task<int> SaveChangesAsync();
/// <summary>
/// 开启事务
/// </summary>
void BeginTransaction();
/// <summary>
/// 提交事务
/// </summary>
void CommitTransaction();
/// <summary>
/// 回滚事务
/// </summary>
void RollBackTransaction();
}
2、定义 IUnitOfWork的实现类UnitOfWork,定义泛型TDBContext其类型必须是DbContext,通过构造函数依赖注入_dbContext
public class UnitOfWork<TDBContext> : IUnitOfWork where TDBContext : DbContext
{
private readonly TDBContext _dbContext;
public UnitOfWork(TDBContext dbContext)
{
_dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext));
}
public int SaveChanges()
{
return _dbContext.SaveChanges();
}
public async Task<int> SaveChangesAsync()
{
return await _dbContext.SaveChangesAsync();
}
public void BeginTransaction()
{
_dbContext.Database.BeginTransaction();
}
public void CommitTransaction()
{
_dbContext.Database.CommitTransaction();
}
public void RollBackTransaction()
{
_dbContext.Database.RollbackTransaction();
}
}
3、修改仓储的实现类,其实现类的链接在文章顶部已经实现了,但是要把 this._context.SaveChanges();这行代码全部删除。这样就不会每次更改就会提交,而是交由IUnitOfWork 统一的提交事务。
4、注册IUnitOfWork的时候要注意,要确保 UnitOfWork和DBContext 在整个请求中共用同一个对象,这样才能通过IUnitOfWork 来一次性提交事务。AddDbContext这个方法它默认的生命周期为ServiceLifetime.Scoped,所以只需要注册IUnitOfWork的时候使用AddScoped方法,将其生命周期限定为Scoped,就能保证每次请求都是共用同一个对象。
services.AddDbContext<YunSourseContext>(option => option.UseSqlServer(Configuration.GetConnectionString("Default"), b => b.UseRowNumberForPaging()));//配置sqlserver
services.AddScoped<IUnitOfWork,UnitOfWork<YunSourseContext>>();//注入UOW依赖,确保每次请求都是同一个对象
5、代码改造完毕,接下来就要就行实例演示了
1)通过构造函数依赖注入 _userIRepository 和 _unitOfWork ,将事务统一交由 _unitOfWork 来管理
private readonly IRepository<User> _userIRepository;
private readonly IUnitOfWork _unitOfWork;
public UserManager(IRepository<User> userIRepository,IUnitOfWork unitOfWork)
{
_userIRepository = userIRepository;
_unitOfWork = unitOfWork;
2)连续插入两个实体:没有执行_unitOfWork.SaveChanges()之前,数据库中没有新增的数据,只有执行了_unitOfWork.SaveChanges(),数据库才新增了两条数据,说明只提交了一次更改,符合预期目的。
var user = new User()
{
UserName = "1",
UserPwd = "12",
CreationTime = DateTime.Now,
};
var user1 = new User()
{
UserName = "11",
UserPwd = "121",
CreationTime = DateTime.Now,
};
await _userIRepository.InsertAsync(user);
await _userIRepository.InsertAsync(user1);
_unitOfWork.SaveChanges();
3)进行事务操作:如下所示,InsertAndGetId这个方法,会将实体提交到数据库,并返回实体的主键Id,如果后面执行的过程中发生了异常,就需要回滚数据。用于数据的强一致性应用场景。
var user = new User()
{
UserName = "1111",
UserPwd = "12",
CreationTime = DateTime.Now,
};
var user1 = new User()
{
UserName = "11",
UserPwd = "121",
CreationTime = DateTime.Now,
};
try
{
_unitOfWork.BeginTransaction();//开启事务
var userId = _userIRepository.InsertAndGetId(user);//获取UserId,此时已经提交到数据库
await _userIRepository.InsertAsync(user1);
await _unitOfWork.SaveChangesAsync();//提交变更
_unitOfWork.CommitTransaction();//提交事务
}
catch
{
_unitOfWork.RollBackTransaction();//回滚事务
throw;
}