Unit of Work 即工做单元。 用来维护一组受业务影响的对象列表,将多个操做放在一个单元中,把操做原子化,经过事务统一完成一次提交,若是某个过程出现异常,就将全部修改进行回滚,保证数据的有效状态。同时减小了应用程序与数据库通讯,有利于提高系统的性能。web
总体项目结构预览
构建UnitOfWork.Infrastructure
一、新建Domain文件夹,添加IAggregateRoot接口数据库
IAggregateRoot接口属于聚合根,全部业务对象(Entity)都须要实现聚合根。外部对象须要访问聚合内的实体时,只能经过聚合根进行访问,而不能直接访问。领域模型须要根据领域概念分红多个聚合,每一个聚合都有一个实体做为聚合根,通俗的说,领域对象从无到有的建立,以及CRUD的操做都应该做用于聚合根上,而不是单独的某个实体。api
二、新建UnitofWork文件夹,添加IUnitofWorkRepository接口函数
void PersistCreationOf(IAggregateRoot entity); void PersistUpdateOf(IAggregateRoot entity); void PersistDeletionOf(IAggregateRoot entity);三、添加IUnitofWork接口 注意在添加/修改/删除的方法中传入IUnitofWorkRepository,在提交时Unit Of Work将真正持久化工做放在IUnitofWorkRepository的具体类实现中进行性能
void RegisterUpdate(IAggregateRoot entity, IUnitOfWorkRepository unitOfWorkRepository); void RegisterAdd(IAggregateRoot entity, IUnitOfWorkRepository unitOfWorkRepository); void RegisterRemove(IAggregateRoot entity, IUnitOfWorkRepository unitOfWorkRepository) void Commit();
构建UnitOfWork.Model
一、新建Account类并实现IAggregateRoot接口 a.定义公共的属性Balace(余额) b.定义带参数(balace) 的构造函数this
二、添加IAccountRepository接口,将Account持久化 定义添加/更新/删除方法spa
三、新建AccoutService类 定义公共的IAccountRepository对象和IUnitOfWork对象,经过其构造器实现依赖注入code
public IAccountRepository _accountRepository; public IUnitOfWork _unitOfWork; public AccoutService(IAccountRepository accountRepository, IUnitOfWork unitOfWork) { _accountRepository = accountRepository; _unitOfWork = unitOfWork; } /// <summary> /// 转帐 /// </summary> /// <param name="from"></param> /// <param name="to"></param> /// <param name="account"></param> public void Transfer(Account from ,Account to ,decimal balace) { if (from.Balace > balace) { from.Balace -= balace; to.Balace += balace; _accountRepository.Save(from); _accountRepository.Save(to); _unitOfWork.Commit(); } }
构造UnitOfWork.Repository
一、添加AccountRepository类,AccountRepository实现了IAccountRepository和IUnitofWorkRepository接口,IAccountRepository方法的实现简单地将工做委托至Unit Of Work(传入待持久化实体以及Repository的引用),IUnitofWorkRepository方法中实现了真正的持久化任务,具体的持久化方式能够用ADO.NET/EF/NH等。orm
public class AccountRepository:IAccountRepository ,IUnitOfWorkRepository { private IUnitOfWork _unitOfWork; public AccountRepository(IUnitOfWork unitOfWork) { _unitOfWork = unitOfWork; } public void Save(Account account) { _unitOfWork.RegisterAdd(account,this); } public void Update(Account account) { _unitOfWork.RegisterUpdate(account,this); } public void Remove(Account account) { _unitOfWork.RegisterRemove(account,this); } public void PersistCreationOf(Infrastructure.Domain.IAggregateRoot entity) { //ADO.NET/EF/NH } public void PersistUpdateOf(Infrastructure.Domain.IAggregateRoot entity) { //ADO.NET/EF/NH } public void PersistDeletionOf(Infrastructure.Domain.IAggregateRoot entity) { //ADO.NET/EF/NH } }二、添加NHUnitOfWork类,实现IUnitofWork接口对象
public class NHUnitOfWork : IUnitOfWork { public Dictionary<IAggregateRoot, IUnitOfWorkRepository> addedEntitys; public Dictionary<IAggregateRoot, IUnitOfWorkRepository> changedEntitys; public Dictionary<IAggregateRoot, IUnitOfWorkRepository> deletedEntitys; public NHUnitOfWork() { addedEntitys = new Dictionary<IAggregateRoot, IUnitOfWorkRepository>(); changedEntitys = new Dictionary<IAggregateRoot, IUnitOfWorkRepository>(); deletedEntitys = new Dictionary<IAggregateRoot, IUnitOfWorkRepository>(); } public void RegisterUpdate(IAggregateRoot entity, IUnitOfWorkRepository unitOfWorkRepository) { if (!changedEntitys.ContainsKey(entity)) { changedEntitys.Add(entity,unitOfWorkRepository); } } public void RegisterAdd(IAggregateRoot entity, IUnitOfWorkRepository unitOfWorkRepository) { if (!addedEntitys.ContainsKey(entity)) { addedEntitys.Add(entity,unitOfWorkRepository); } } public void RegisterRemove(IAggregateRoot entity, IUnitOfWorkRepository unitOfWorkRepository) { if (!deletedEntitys.ContainsKey(entity)) { deletedEntitys.Add(entity,unitOfWorkRepository); } } public void Commit() { using (TransactionScope scope = new TransactionScope()) { foreach (IAggregateRoot entity in this.addedEntitys.Keys) { this.addedEntitys[entity].PersistCreationOf(entity); } foreach (IAggregateRoot entity in this.changedEntitys.Keys) { this.changedEntitys[entity].PersistUpdateOf(entity); } foreach (IAggregateRoot entity in this.deletedEntitys.Keys) { this.deletedEntitys[entity].PersistDeletionOf(entity); } scope.Complete(); } } }NHUnitOfWork类使用3个字典变量来记录对业务实体的代执行修改。addedEntitys 对应于被添加到数据存储的实体,changedEntitys 处理更新的实体,deletedEntitys处理实体删除,与字典中的实体键匹配的IUnitOfWorkRepository将被保存下来,并用于 Commit 方法之中 ,来调用Repository对象,该对象包含真正持久化实体的代码 。Commit方法遍历每个字典,并调用相应的IUnitOfWorkRepository方法(传递实体引用)。Commit方法中的工做均被 TransactionScope 代码包装起来,若是在IUnitOfWorkRepository中执行任务时出现异常,则全部工做回滚,数据存储将保持原来的状态。
构建UnitOfWork.Console
一、添加控制台项目用于验证UnitofWork模式效果
static void Main(string[] args) { Account a = new Account(1000); System.Console.WriteLine("如今a,存有{0}",a.Balace); Account b = new Account(200); System.Console.WriteLine("如今b,存有{0}",b.Balace); System.Console.WriteLine("a转给b 500元,开始转帐......"); IUnitOfWork unitOfWork = new NHUnitOfWork(); IAccountRepository accountRepository = new AccountRepository(unitOfWork); AccoutService service = new AccoutService(accountRepository,unitOfWork); service.Transfer(a,b,500); System.Console.WriteLine("转帐结束"); System.Console.WriteLine("a当前金额:{0}",a.Balace); System.Console.WriteLine("b当前金额:{0}",b.Balace); System.Console.ReadKey(); }执行后结果以下:
![]()
对于Unit of Work模式是一种解决方案,一种思路,每一个项目的环境都是不同的,因此咱们须要理解的就是其中的原理。以上实例主要是讲解如何部署UnitofWork模式,便于你们的理解,谢谢!
疑问讨论
上面讲的是网上资料中最广泛的说法,对经典的Unit of Work模式进行了说明。可是我我的有一个观点须要和你们探讨一下:
先说一个问题,你们在初次接触Unit of Work的时候,会不会有一个疑问——这个和数据库事务有什么区别?
而后我再说个人观点吧。
1.我以为Unit of Work就是一个思想,工做单元及原子性操做,而这个关联到数据库中,也就是事务了,因此我以为数据库事务是Unit of Work在数据库中的一种体现。
2.上面讲述的Unit of Work是网上最多见的一种说法。我我的以为,上面那只是一种实现方式,而在网上看到大部分关于Unit of Work的优势中“减小数据库链接、减小数据库链接时间”,这些都是这种实现方式的优势。可是我认为这并非Unit of Work真正的意图,我认为Unit of Work只是单纯的原子操做思想,就像我认为微软提供的TransactionScope也是Unit of Work的一种实现同样。
3.我认为上述的这种广泛的说法可能存在这样的问题:
前置条件:数据库表主键为自增的int型ID。
场景: A表一条数据执行了修改操做,B表执行了一个新增操做,接下来有一个if判断,一条分支是拿B表新增数据的ID调用一个webapi,在以后还有一个对C表的修改操做,另外一条分支是在D表中新增一条数据。
问题:由于全部的增删改操做都会被延后到工做单元结束的地方进行执行,因此在代码执行到往B表插入数据的时候,实际上没有插入,这时若是在if判断的地方走了第一个分支,用B表插入的数据ID去调用webapi,这将会出现问题。
解决方案:
1.不适用自增ID,好比换成GUID;
2.为webapi实现一个上述的Unit of Work的适配器,将api操做也压入集合中,等到后面一块儿处理;
3.在工做单元开始的时候就开启一个数据库事务。
第一个解决方案在一些成型系统中再遇到就是不可行的,第二个方案会稍显麻烦,第三个方案就不存在以前说的“减小数据库链接时间”的优势了。
以上是我我的对Unit of Work的观点与疑问,但愿你们可以各发己见,一块儿探讨一下。