企业模式之Unit Of Work模式 - 烧点饭 - 博客园
Excerpt 在开始UnitOfWork模式之前有必要回顾下我们耳熟能详的Data Access Object(DAO)模式,即数据访问对象。DAO是一种简单的模式,我们构建应用的时候经常会使用到它,它的功能就是将DAL元素从应用程序中分离出来,在经典的三层架构中,我们会将数据持久化工作单独分离出来,封装成DAL
在开始UnitOfWork模式之前有必要回顾下我们耳熟能详的Data Access Object(DAO)模式,即数据访问对象。DAO是一种简单的模式,我们构建应用的时候经常会使用到它,它的功能就是将DAL元素从应用程序中分离出来,在经典的三层架构中,我们会将数据持久化工作单独分离出来,封装成DAL层。但是,DAO并没有隐藏它面对是一张张数据表,而且通常情况我们会为数据库中的每一张表创建一个DAO类,想必大家对这种方式的极度的不爽了,。
由于DAO模式与数据表是一对一匹配的关系,因此DAO模式很好的配合了Active Record和Transaction Script业务模式,尤其是Table Module。正因为这种与数据表一对一匹配关系,使我对DAO模式深恶痛绝。
Unit Of Work模式,即工作单元,它是一种数据访问模式。它是用来维护一个由已经被业务修改(如增加、删除和更新等)的业务对象组成的列表。它负责协调这些业务对象的持久化工作及并发问题。那它是怎么来维护的一系列业务对象组成的列表持久化工作的呢?通过事务。Unit Of Work模式会记录所有对象模型修改过的信息,在提交的时候,一次性修改,并把结果同步到数据库。 这个过程通常被封装在事务中。所以在DAL中采用Unit Of Work模式好处就在于能够确保数据的完整性,如果在持有一系列业务对象(同属于一个事务)的过程中出现问题,就可以将所有的修改回滚,以确保数据始终处于有效状态,不会出现脏数据。
在这里我们,使用一个简单的银行领域对两个帐号之间的转账进行举例
首先如图进行分层搭建基础框架
总共分为四层依次是: —> 引用关系
UnitOfWork.Console —>UnitOfWork.Infrastructure、UnitOfWork.Model、UnitOfWork.Repository
UnitOfWork.Infrastructure
UnitOfWork.Model —>UnitOfWork.Infrastructure
UnitOfWork.Repository —>UnitOfWork.Model、UnitOfWork.Infrastructure
这不是经典的领域驱动架构,因为业务简单就进行简单搭建。
UnitOfWork.Infrastructure:
首先,在UnitOfWork.Infrastructure建了一个Domain文件夹,里面只建了一个IAggregateRoot接口,Unit Of Work操作的实体必须是实现IAggregateRoot接口的。
1 2 3 4 5 6 7 <span>///</span> <span><summary></span> <span>///</span><span> 标识接口,定义聚合根 </span><span>///</span> <span></summary></span> <span>public</span> <span>class</span><span> IAggregateRoot { }</span>
建立UnitOfWork文件夹,在里面添加一个IUnitOfWorkRepository接口
1 2 3 4 5 6 7 8 9 <span>public</span> <span>interface</span><span> IUnitOfWorkRepository { </span><span>//</span><span>新增</span> <span>void</span><span> PersistCreationOf(IAggregateRoot entity); </span><span>//</span><span>更新</span> <span>void</span><span> PersistUpdateOf(IAggregateRoot entity); </span><span>//</span><span>删除</span> <span>void</span><span> PersistDeletionOf(IAggregateRoot entity); }</span>
我们再向UnitOfWork文件夹中添加一个IUnitOfWork接口,IUnitOfWork接口在注册更新、新增和删除时,需要IUnitOfWorkRepository,这样再提交Commit,UnitOfWork可以将真正持久化的工作委托给适合的具体实现对象,其实就是将持久化工作交给了IUnitOfWorkRepository的实现类,我们稍后看看IUnitOfWork的实现类你就清楚了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 <span> public</span> <span>interface</span><span> IUnitOfWork { </span><span>///</span> <span><summary></span> <span>///</span><span> 更新 </span><span>///</span> <span></summary></span> <span>///</span> <span><param name="entity"></param></span> <span>///</span> <span><param name="unitofWorkRepository"></param></span> <span>void</span><span> RegisterUpdate(IAggregateRoot entity, IUnitOfWorkRepository unitofWorkRepository); </span><span>///</span> <span><summary></span> <span>///</span><span> 新增 </span><span>///</span> <span></summary></span> <span>///</span> <span><param name="entity"></param></span> <span>///</span> <span><param name="unitofWorkRepository"></param></span> <span>void</span><span> RegisterAdd(IAggregateRoot entity, IUnitOfWorkRepository unitofWorkRepository); </span><span>///</span> <span><summary></span> <span>///</span><span> 删除 </span><span>///</span> <span></summary></span> <span>///</span> <span><param name="entity"></param></span> <span>///</span> <span><param name="unitofWorkRepository"></param></span> <span>void</span><span> RegisterRemoved(IAggregateRoot entity, IUnitOfWorkRepository unitofWorkRepository); </span><span>///</span> <span><summary></span> <span>///</span><span> 提交 </span><span>///</span> <span></summary></span> <span>void</span><span> Commit(); }</span>
顺便说一句,在UnitOfWork.Infrastructure,我还建立的Common文件夹,Logging文件夹,所要表达的意思是Infrastructure意为基础设施层,我们可以将通用的组件放到这里面,比如日志组件,邮件组件等。
好了,回顾下,我们在UnitOfWork.Infrastructure中其实什么业务都没有处理,只定义了三个接口。
UnitOfWork.Model
接下来,说说这层干了什么事吧!这一层主要是处理两个账户之间转账的业务。
向UnitOfWork.Model中添加一个Account类,实现IAggregateRoot接口,表示它是一个聚合根,可以被Unit Of Work操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <span>///</span> <span><summary></span> <span>///</span><span> 账户类,表示Account是集合根 </span><span>///</span> <span></summary></span> <span>public</span> <span>class</span><span> Account : IAggregateRoot { </span><span>public</span> Account(<span>decimal</span><span> balance) { Balance </span>=<span> balance; } </span><span>///</span> <span><summary></span> <span>///</span><span> 账户余额 </span><span>///</span> <span></summary></span> <span>public</span> <span>decimal</span> Balance { <span>get</span>; <span>set</span><span>; } }</span>
为了持久化Account类,我们添加的一个仅包含了示例有关的的Repository接口,即IAccountRepository,只是简单的示例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <span>///</span> <span><summary></span> <span>///</span><span> 定义持久化操作 </span><span>///</span> <span></summary></span> <span>public</span> <span>interface</span><span> IAccountRepository { </span><span>///</span> <span><summary></span> <span>///</span><span> 更新 </span><span>///</span> <span></summary></span> <span>///</span> <span><param name="account"></param></span> <span>void</span><span> Save(Account account); </span><span>///</span> <span><summary></span> <span>///</span><span> 新增 </span><span>///</span> <span></summary></span> <span>///</span> <span><param name="account"></param></span> <span>void</span><span> Add(Account account); </span><span>///</span> <span><summary></span> <span>///</span><span> 删除 </span><span>///</span> <span></summary></span> <span>///</span> <span><param name="account"></param></span> <span>void</span><span> Remove(Account account); }</span>
为了完成转账的业务,我们需要创建一个服务类来协调两个账户之间的转账工作,如AccountService类,在AccountService类中,通过构造函数初始化了IAccountRepository和IUnitOfWork。在完成转账后,它们都调用了账户Repository进行持久化。最后,通过Unit Of Work的Commit来确保该笔交易的完成。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 <span>public</span> <span>class</span><span> AccountService { </span><span>private</span><span> IAccountRepository _accountRepository; </span><span>private</span><span> IUnitOfWork _unitOfWork; </span><span>public</span><span> AccountService(IAccountRepository accountRepository, IUnitOfWork unitOfWork) { _accountRepository </span>=<span> accountRepository; _unitOfWork </span>=<span> unitOfWork; } </span><span>///</span> <span><summary></span> <span>///</span><span> 转账 </span><span>///</span> <span></summary></span> <span>///</span> <span><param name="from"></param></span> <span>///</span> <span><param name="to"></param></span> <span>///</span> <span><param name="amount"></param></span> <span>public</span> <span>void</span> Transfer(Account <span>from</span>, Account to, <span>decimal</span><span> amount) { </span><span>if</span> (<span>from</span>.Balance >=<span> amount) { </span><span>from</span>.Balance -=<span> amount; to.Balance </span>+=<span> amount; _accountRepository.Save(</span><span>from</span><span>); _accountRepository.Save(to); _unitOfWork.Commit(); } } }</span>
总结下,在UnitOfWork.Model中我们定义的一个Account类,定义了一个持久化Account类的接口,以及转账业务的完成。下面我们要进入UnitOfWork.Repository探究Repository和Unit Of Work之间是怎么交互的?
UnitOfWork.Repository:
在UnitOfWork.Repository,添加了一个NHUnitOfWork类,实现了UnitOfWork.Infrastructure.UnitOfWork中的IUnitOfWork接口,为什么定义到这里。因为在项目开发中你可能有NHbernator的Repository和EF的Repository。还记得我在讲解IUnitOfWork接口时,曾说过这样一句话“IUnitOfWork将持久化工作交给了IUnitOfWorkRepository的实现类”,在这里你就会找到答案了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 <span>public</span> <span>class</span><span> NHUnitOfWork : IUnitOfWork { </span><span>private</span> Dictionary<IAggregateRoot, IUnitOfWorkRepository><span> addedEntities; </span><span>private</span> Dictionary<IAggregateRoot, IUnitOfWorkRepository><span> changedEntities; </span><span>private</span> Dictionary<IAggregateRoot, IUnitOfWorkRepository><span> deletedEntities; </span><span>public</span><span> NHUnitOfWork() { addedEntities </span>= <span>new</span> Dictionary<IAggregateRoot, IUnitOfWorkRepository><span>(); changedEntities </span>= <span>new</span> Dictionary<IAggregateRoot, IUnitOfWorkRepository><span>(); deletedEntities </span>= <span>new</span> Dictionary<IAggregateRoot, IUnitOfWorkRepository><span>(); } </span><span>public</span> <span>void</span><span> RegisterUpdate(IAggregateRoot entity, IUnitOfWorkRepository unitofWorkRepository) { </span><span>if</span> (!<span>changedEntities.ContainsKey(entity)) { changedEntities.Add(entity, unitofWorkRepository); } } </span><span>public</span> <span>void</span><span> RegisterAdd(IAggregateRoot entity, IUnitOfWorkRepository unitofWorkRepository) { </span><span>if</span> (!<span>addedEntities.ContainsKey(entity)) { addedEntities.Add(entity, unitofWorkRepository); }; } </span><span>public</span> <span>void</span><span> RegisterRemoved(IAggregateRoot entity, IUnitOfWorkRepository unitofWorkRepository) { </span><span>if</span> (!<span>deletedEntities.ContainsKey(entity)) { deletedEntities.Add(entity, unitofWorkRepository); } } </span><span>public</span> <span>void</span><span> Commit() { </span><span>using</span> (TransactionScope scope = <span>new</span><span> TransactionScope()) { </span><span>foreach</span> (IAggregateRoot entity <span>in</span> <span>this</span><span>.addedEntities.Keys) { </span><span>this</span><span>.addedEntities[entity].PersistCreationOf(entity); } </span><span>foreach</span> (IAggregateRoot entity <span>in</span> <span>this</span><span>.changedEntities.Keys) { </span><span>this</span><span>.changedEntities[entity].PersistUpdateOf(entity); } </span><span>foreach</span> (IAggregateRoot entity <span>in</span> <span>this</span><span>.deletedEntities.Keys) { </span><span>this</span><span>.deletedEntities[entity].PersistDeletionOf(entity); } scope.Complete(); } }</span>
接下来,再添加一个AccountRepository类,这个类实现了两个接口,IAccountRepository和IUnitOfWorkRepository接口。IAccountRepository中的方法就是简单得将工作交给了Unit Of Work,传入待持久化的实体及Repository(实现了IUnitOfWorkRepository)引用。最后Unit Of Work 引用Repository的IUnitOfWorkRepository契约来完成真正的持久化工作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 <span>public</span> <span>class</span><span> AccountRepository : IAccountRepository,IUnitOfWorkRepository { </span><span>private</span><span> IUnitOfWork _unitOfWork; </span><span>public</span><span> AccountRepository(IUnitOfWork unitOfWork) { _unitOfWork </span>=<span> unitOfWork; } </span><span>public</span> <span>void</span><span> Save(Account account) { _unitOfWork.RegisterUpdate(account, </span><span>this</span><span>); } </span><span>public</span> <span>void</span><span> Add(Account account) { _unitOfWork.RegisterAdd(account, </span><span>this</span><span>); } </span><span>public</span> <span>void</span><span> Remove(Account account) { _unitOfWork.RegisterRemoved(account, </span><span>this</span><span>); } </span><span>public</span> <span>void</span><span> PersistUpdateOf(IAggregateRoot entity) { </span><span>//</span><span> ADO.net code to update the entity...真正的SQL实现</span> <span> } </span><span>public</span> <span>void</span><span> PersistCreationOf(IAggregateRoot entity) { </span><span>//</span><span> ADO.net code to Add the entity...真正的SQL实现</span> <span> } </span><span>public</span> <span>void</span><span> PersistDeletionOf(IAggregateRoot entity) { </span><span>//</span><span> ADO.net code to delete the entity...真正的SQL实现</span> <span> } } </span>
总结下,UnitOfWork.Repository这里才是真正纠结的地方。首先,Unit Of Work加载实体对象(Accont)和实体对应的Repository对象(AccountRepository);然后通过Unit Of Work的Commit方法,循环转调Repository对象(AccountRepository)的持久化方法,进行实体对象(Accont)的持久化工作。多调试就明白了。
UnitOfWork.Console:
接下来,就是最后的控制台程序了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 <span>class</span><span> Program { </span><span>static</span> <span>void</span> Main(<span>string</span><span>[] args) { Account a </span>= <span>new</span> Account(<span>1000</span><span>); System.Console.WriteLine(</span><span>"</span><span>现在张三,存有{0}</span><span>"</span><span>, a.Balance); Account b </span>= <span>new</span> Account(<span>200</span><span>); System.Console.WriteLine(</span><span>"</span><span>现在李四,存有{0}</span><span>"</span><span>, b.Balance); System.Console.WriteLine(</span><span>"</span><span>张三准备转500元给李四,转战开始了......</span><span>"</span><span>); </span><span>//</span><span>声明要使用的UnitOfWork</span> IUnitOfWork nhUnitOfWork = <span>new</span><span> NHUnitOfWork(); </span><span>//</span><span>声明要使用的Repository</span> IAccountRepository accountRepository = <span>new</span><span> AccountRepository(nhUnitOfWork); AccountService service </span>= <span>new</span><span> AccountService(accountRepository, nhUnitOfWork); service.Transfer(a,b,</span><span>500</span><span>); System.Console.WriteLine(</span><span>"</span><span>转账结束</span><span>"</span><span>); System.Console.WriteLine(</span><span>"</span><span>张三当前余额:{0}</span><span>"</span><span>,a.Balance); System.Console.WriteLine(</span><span>"</span><span>李四当前余额:{0}</span><span>"</span><span>,b.Balance); System.Console.ReadKey(); } }</span>
好了,睡觉了!