UnitOfWork实战
企业模式之Unit Of Work模式
在开始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接口的。

- /// <summary>
- /// 标识接口,定义聚合根
- /// </summary>
- public class IAggregateRoot
- {
- }

建立UnitOfWork文件夹,在里面添加一个IUnitOfWorkRepository接口

- public interface IUnitOfWorkRepository
- {
- //新增
- void PersistCreationOf(IAggregateRoot entity);
- //更新
- void PersistUpdateOf(IAggregateRoot entity);
- //删除
- void PersistDeletionOf(IAggregateRoot entity);
- }

我们再向UnitOfWork文件夹中添加一个IUnitOfWork接口,IUnitOfWork接口在注册更新、新增和删除时,需要 IUnitOfWorkRepository,这样再提交Commit,UnitOfWork可以将真正持久化的工作委托给适合的具体实现对象,其实就是 将持久化工作交给了IUnitOfWorkRepository的实现类,我们稍后看看IUnitOfWork的实现类你就清楚了。

- public interface IUnitOfWork
- {
- /// <summary>
- /// 更新
- /// </summary>
- /// <param name="entity"></param>
- /// <param name="unitofWorkRepository"></param>
- void RegisterUpdate(IAggregateRoot entity, IUnitOfWorkRepository unitofWorkRepository);
- /// <summary>
- /// 新增
- /// </summary>
- /// <param name="entity"></param>
- /// <param name="unitofWorkRepository"></param>
- void RegisterAdd(IAggregateRoot entity, IUnitOfWorkRepository unitofWorkRepository);
- /// <summary>
- /// 删除
- /// </summary>
- /// <param name="entity"></param>
- /// <param name="unitofWorkRepository"></param>
- void RegisterRemoved(IAggregateRoot entity, IUnitOfWorkRepository unitofWorkRepository);
- /// <summary>
- /// 提交
- /// </summary>
- void Commit();
- }

顺便说一句,在UnitOfWork.Infrastructure,我还建立的Common文件夹,Logging文件夹,所要表达的意思是Infrastructure意为基础设施层,我们可以将通用的组件放到这里面,比如日志组件,邮件组件等。
好了,回顾下,我们在UnitOfWork.Infrastructure中其实什么业务都没有处理,只定义了三个接口。
UnitOfWork.Model
接下来,说说这层干了什么事吧!这一层主要是处理两个账户之间转账的业务。
向UnitOfWork.Model中添加一个Account类,实现IAggregateRoot接口,表示它是一个聚合根,可以被Unit Of Work操作。

- /// <summary>
- /// 账户类,表示Account是集合根
- /// </summary>
- public class Account : IAggregateRoot
- {
- public Account(decimal balance)
- {
- Balance = balance;
- }
- /// <summary>
- /// 账户余额
- /// </summary>
- public decimal Balance { get; set; }
- }

为了持久化Account类,我们添加的一个仅包含了示例有关的的Repository接口,即IAccountRepository,只是简单的示例。

- /// <summary>
- /// 定义持久化操作
- /// </summary>
- public interface IAccountRepository
- {
- /// <summary>
- /// 更新
- /// </summary>
- /// <param name="account"></param>
- void Save(Account account);
- /// <summary>
- /// 新增
- /// </summary>
- /// <param name="account"></param>
- void Add(Account account);
- /// <summary>
- /// 删除
- /// </summary>
- /// <param name="account"></param>
- void Remove(Account account);
- }

为了完成转账的业务,我们需要创建一个服务类来协调两个账户之间的转账工作,如AccountService类,在 AccountService类中,通过构造函数初始化了IAccountRepository和IUnitOfWork。在完成转账后,它们都调用了账 户Repository进行持久化。最后,通过Unit Of Work的Commit来确保该笔交易的完成。

- public class AccountService
- {
- private IAccountRepository _accountRepository;
- private IUnitOfWork _unitOfWork;
- public AccountService(IAccountRepository accountRepository,
- IUnitOfWork unitOfWork)
- {
- _accountRepository = accountRepository;
- _unitOfWork = unitOfWork;
- }
- /// <summary>
- /// 转账
- /// </summary>
- /// <param name="from"></param>
- /// <param name="to"></param>
- /// <param name="amount"></param>
- public void Transfer(Account from, Account to, decimal amount)
- {
- if (from.Balance >= amount)
- {
- from.Balance -= amount;
- to.Balance += amount;
- _accountRepository.Save(from);
- _accountRepository.Save(to);
- _unitOfWork.Commit();
- }
- }
- }

总结下,在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的实现类”,在这里你就会找到答案了。

- public class NHUnitOfWork : IUnitOfWork
- {
- private Dictionary<IAggregateRoot, IUnitOfWorkRepository> addedEntities;
- private Dictionary<IAggregateRoot, IUnitOfWorkRepository> changedEntities;
- private Dictionary<IAggregateRoot, IUnitOfWorkRepository> deletedEntities;
- public NHUnitOfWork()
- {
- addedEntities = new Dictionary<IAggregateRoot, IUnitOfWorkRepository>();
- changedEntities = new Dictionary<IAggregateRoot, IUnitOfWorkRepository>();
- deletedEntities = new Dictionary<IAggregateRoot, IUnitOfWorkRepository>();
- }
- public void RegisterUpdate(IAggregateRoot entity, IUnitOfWorkRepository unitofWorkRepository)
- {
- if (!changedEntities.ContainsKey(entity))
- {
- changedEntities.Add(entity, unitofWorkRepository);
- }
- }
- public void RegisterAdd(IAggregateRoot entity, IUnitOfWorkRepository unitofWorkRepository)
- {
- if (!addedEntities.ContainsKey(entity))
- {
- addedEntities.Add(entity, unitofWorkRepository);
- };
- }
- public void RegisterRemoved(IAggregateRoot entity, IUnitOfWorkRepository unitofWorkRepository)
- {
- if (!deletedEntities.ContainsKey(entity))
- {
- deletedEntities.Add(entity, unitofWorkRepository);
- }
- }
- public void Commit()
- {
- using (TransactionScope scope = new TransactionScope())
- {
- foreach (IAggregateRoot entity in this.addedEntities.Keys)
- {
- this.addedEntities[entity].PersistCreationOf(entity);
- }
- foreach (IAggregateRoot entity in this.changedEntities.Keys)
- {
- this.changedEntities[entity].PersistUpdateOf(entity);
- }
- foreach (IAggregateRoot entity in this.deletedEntities.Keys)
- {
- this.deletedEntities[entity].PersistDeletionOf(entity);
- }
- scope.Complete();
- }
- }

接下来,再添加一个AccountRepository类,这个类实现了两个接口,IAccountRepository和 IUnitOfWorkRepository接口。IAccountRepository中的方法就是简单得将工作交给了Unit Of Work,传入待持久化的实体及Repository(实现了IUnitOfWorkRepository)引用。最后Unit Of Work 引用Repository的IUnitOfWorkRepository契约来完成真正的持久化工作。

- public class AccountRepository : IAccountRepository,IUnitOfWorkRepository
- {
- private IUnitOfWork _unitOfWork;
- public AccountRepository(IUnitOfWork unitOfWork)
- {
- _unitOfWork = unitOfWork;
- }
- public void Save(Account account)
- {
- _unitOfWork.RegisterUpdate(account, this);
- }
- public void Add(Account account)
- {
- _unitOfWork.RegisterAdd(account, this);
- }
- public void Remove(Account account)
- {
- _unitOfWork.RegisterRemoved(account, this);
- }
- public void PersistUpdateOf(IAggregateRoot entity)
- {
- // ADO.net code to update the entity...真正的SQL实现
- }
- public void PersistCreationOf(IAggregateRoot entity)
- {
- // ADO.net code to Add the entity...真正的SQL实现
- }
- public void PersistDeletionOf(IAggregateRoot entity)
- {
- // ADO.net code to delete the entity...真正的SQL实现
- }
- }

总结下,UnitOfWork.Repository这里才是真正纠结的地方。首先,Unit Of Work加载实体对象(Accont)和实体对应的Repository对象(AccountRepository);然后通过Unit Of Work的Commit方法,循环转调Repository对象(AccountRepository)的持久化方法,进行实体对象(Accont)的持 久化工作。多调试就明白了。
UnitOfWork.Console:
接下来,就是最后的控制台程序了。

- class Program
- {
- static void Main(string[] args)
- {
- Account a = new Account(1000);
- System.Console.WriteLine("现在张三,存有{0}", a.Balance);
- Account b = new Account(200);
- System.Console.WriteLine("现在李四,存有{0}", b.Balance);
- System.Console.WriteLine("张三准备转500元给李四,转战开始了......");
- //声明要使用的UnitOfWork
- IUnitOfWork nhUnitOfWork = new NHUnitOfWork();
- //声明要使用的Repository
- IAccountRepository accountRepository = new AccountRepository(nhUnitOfWork);
- AccountService service = new AccountService(accountRepository, nhUnitOfWork);
- service.Transfer(a,b,500);
- System.Console.WriteLine("转账结束");
- System.Console.WriteLine("张三当前余额:{0}",a.Balance);
- System.Console.WriteLine("李四当前余额:{0}",b.Balance);
- System.Console.ReadKey();
- }
- }

UnitOfWork实战的更多相关文章
- 【无私分享:ASP.NET CORE 项目实战(第五章)】Repository仓储 UnitofWork
目录索引 [无私分享:ASP.NET CORE 项目实战]目录索引 简介 本章我们来创建仓储类Repository 并且引入 UnitOfWork 我对UnitOfWork的一些理解 UnitOfW ...
- 【asp.net core 系列】9 实战之 UnitOfWork以及自定义代码生成
0. 前言 在前一篇中我们创建了一个基于EF的数据查询接口实现基类,这一篇我将带领大家讲一下为这EF补充一些功能,并且提供一个解决避免写大量配置类的方案. 1. SaveChanges的外移 在之前介 ...
- 【无私分享:ASP.NET CORE 项目实战】目录索引
简介 首先,我们的 [无私分享:从入门到精通ASP.NET MVC] 系列已经接近尾声,希望大家在这个过程中学到了一些思路和方法,而不仅仅是源码. 因为是第一次写博客,我感觉还是比较混乱的,其中 ...
- 应用程序框架实战二十二 : DDD分层架构之仓储(层超类型基础篇)
前一篇介绍了仓储的基本概念,并谈了我对仓储的一些认识,本文将实现仓储的基本功能. 仓储代表聚合在内存中的集合,所以仓储的接口需要模拟得像一个集合.仓储中有很多操作都是可以通用的,可以把这部分操作抽取到 ...
- Repository仓储 UnitofWork
Repository仓储 UnitofWork 目录索引 [无私分享:ASP.NET CORE 项目实战]目录索引 简介 本章我们来创建仓储类Repository 并且引入 UnitOfWork 我对 ...
- LoadTest中内存和线程Troubleshooting实战
LoadTest中内存和线程Troubleshooting实战 在端午节放假的三天中,我对正在开发的Service进行了LoadTest,尝试在增大压力的条件下发现问题. 该Service为独立进程的 ...
- 一步一步学EF系列 【7、结合IOC ,Repository,UnitOfWork来完成框架的搭建】
前言 距离上一篇已经有段时间了,最近这段时间赶上新项目开发,一直没有时间来写.之前的几篇文章,主要把EF的基础都讲了一遍,这批文章就来个实战篇. 个人在学习过程中参考博客: Entity Framew ...
- 实战框架ABP
abp及实战框架概述 接触abp也快一年了,有过大半年的abp项目开发经验,目前项目中所用的abp框架版本为0.10.3,最新的abp框架已经到了1.4,并且支持了asp.net core.关于abp ...
- 【asp.net core 系列】10 实战之ActionFilter
0.前言 在上一篇中,我们提到了如何创建一个UnitOfWork并通过ActionFilter设置启用.这一篇我们将简单介绍一下ActionFilter以及如何利用ActionFilter,顺便补齐一 ...
随机推荐
- ubuntu server 11.10 mysql 自动备份脚本
1.下载最新的备份脚本(AutoMySQLBackup) 点这里下载 2.修改脚本配置部分 vi /root/automysqlbackup-2.5.1-01.sh USERNAME=root PA ...
- android 5.0新特性学习--视图轮廓
ViewOutlineProvider -- 视图轮廓setOutlineProvider--可以使用View.setClipToOutline方法去剪切一个视图的outline区域.只有rectan ...
- 安装apk时出现错误Failure [INSTALL_FAILED_DEXOPT]问题解决的方法
在android4.0源码里面编译出来apk后,用adb install (或adb install -r 重装)安装时,报错[INSTALL_FAILED_DEXOPT]. xu@xu-PC:~$ ...
- svg都快忘了,复习一下
http://www.360doc.com/content/07/0906/21/39836_724430.shtml
- zf-关于通知公告显示问题
1 公告结束日期超过当前时间是不能在通知公告上显示出来的 2 无限制时间的公告也是要在通知公告上显示出来的 于是我在后台实现类增加了如下代码 需要注意的是 当初解决第一个问题的时候增加了一个AND 当 ...
- jQuery常用及基础知识总结(三)
1.通过jquery的$()引用元素包括通过id.class.元素名以及元素的层级关系及dom或者xpath条件等方法,且返回的对象为jquery对象(集合对象),不能直接调用dom定义的方法. 2. ...
- 实现免密码输入 ssh 登录
实现免密码输入 ssh 登录假设 A 为客户机器, B 为目标机:要达到的目的:A 机器 ssh 登录 B 机器无需输入密码:加密方式选 rsa|dsa 均可以,默认 dsa做法:1.登录 A 机器2 ...
- HRBUST 1909 理工门外的树(双数组实现线段树功能)
线段树固然可以做,但是在考虑线段树之前应该先考虑一下,这种用两个数组来解决问题的方法能不能行,如果行的话,就会为我们省很多时间,毕竟线段树代码量比较大 #include<iostream> ...
- 最完整的自动化测试流程:Python编写执行测试用例及定时自动发送最新测试报告邮件
今天笔者就要归纳总结下一整套测试流程,从无到有,实现零突破,包括如何编写测试用例,定时执行测试用例,查找最新生成的测试报告文件,自动发送最新测试报告邮件,一整套完整的测试流程.以后各位只要着重如何编写 ...
- Android编程中的实用快捷键
作为一个优秀的程序员,不但要能开发出漂亮的软件,也要能熟练掌握编程的技巧,包括IDE的快捷键使用.比如linux 下的VI编辑器,对于不熟练快捷键的人来说就是一个噩梦,但一旦你熟练了VI的快捷键,VI ...