企业模式之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接口的。

  1. /// <summary>
  2. /// 标识接口,定义聚合根
  3. /// </summary>
  4. public class IAggregateRoot
  5. {
  6.  
  7. }

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

  1. public interface IUnitOfWorkRepository
  2. {
  3. //新增
  4. void PersistCreationOf(IAggregateRoot entity);
  5. //更新
  6. void PersistUpdateOf(IAggregateRoot entity);
  7. //删除
  8. void PersistDeletionOf(IAggregateRoot entity);
  9. }

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

  1.   public interface IUnitOfWork
  2. {
  3. /// <summary>
  4. /// 更新
  5. /// </summary>
  6. /// <param name="entity"></param>
  7. /// <param name="unitofWorkRepository"></param>
  8. void RegisterUpdate(IAggregateRoot entity, IUnitOfWorkRepository unitofWorkRepository);
  9. /// <summary>
  10. /// 新增
  11. /// </summary>
  12. /// <param name="entity"></param>
  13. /// <param name="unitofWorkRepository"></param>
  14. void RegisterAdd(IAggregateRoot entity, IUnitOfWorkRepository unitofWorkRepository);
  15. /// <summary>
  16. /// 删除
  17. /// </summary>
  18. /// <param name="entity"></param>
  19. /// <param name="unitofWorkRepository"></param>
  20. void RegisterRemoved(IAggregateRoot entity, IUnitOfWorkRepository unitofWorkRepository);
  21. /// <summary>
  22. /// 提交
  23. /// </summary>
  24. void Commit();
  25. }

  顺便说一句,在UnitOfWork.Infrastructure,我还建立的Common文件夹,Logging文件夹,所要表达的意思是Infrastructure意为基础设施层,我们可以将通用的组件放到这里面,比如日志组件,邮件组件等。

好了,回顾下,我们在UnitOfWork.Infrastructure中其实什么业务都没有处理,只定义了三个接口。

  UnitOfWork.Model

  接下来,说说这层干了什么事吧!这一层主要是处理两个账户之间转账的业务。

  向UnitOfWork.Model中添加一个Account类,实现IAggregateRoot接口,表示它是一个聚合根,可以被Unit Of Work操作。

  1. /// <summary>
  2. /// 账户类,表示Account是集合根
  3. /// </summary>
  4. public class Account : IAggregateRoot
  5. {
  6. public Account(decimal balance)
  7. {
  8. Balance = balance;
  9. }
  10. /// <summary>
  11. /// 账户余额
  12. /// </summary>
  13. public decimal Balance { get; set; }
  14. }

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

  1. /// <summary>
  2. /// 定义持久化操作
  3. /// </summary>
  4. public interface IAccountRepository
  5. {
  6. /// <summary>
  7. /// 更新
  8. /// </summary>
  9. /// <param name="account"></param>
  10. void Save(Account account);
  11. /// <summary>
  12. /// 新增
  13. /// </summary>
  14. /// <param name="account"></param>
  15. void Add(Account account);
  16. /// <summary>
  17. /// 删除
  18. /// </summary>
  19. /// <param name="account"></param>
  20. void Remove(Account account);
  21. }

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

  1. public class AccountService
  2. {
  3. private IAccountRepository _accountRepository;
  4. private IUnitOfWork _unitOfWork;
  5.  
  6. public AccountService(IAccountRepository accountRepository,
  7. IUnitOfWork unitOfWork)
  8. {
  9. _accountRepository = accountRepository;
  10. _unitOfWork = unitOfWork;
  11. }
  12.  
  13. /// <summary>
  14. /// 转账
  15. /// </summary>
  16. /// <param name="from"></param>
  17. /// <param name="to"></param>
  18. /// <param name="amount"></param>
  19. public void Transfer(Account from, Account to, decimal amount)
  20. {
  21. if (from.Balance >= amount)
  22. {
  23. from.Balance -= amount;
  24. to.Balance += amount;
  25.  
  26. _accountRepository.Save(from);
  27. _accountRepository.Save(to);
  28. _unitOfWork.Commit();
  29. }
  30. }
  31. }

  总结下,在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. public class NHUnitOfWork : IUnitOfWork
  2. {
  3. private Dictionary<IAggregateRoot, IUnitOfWorkRepository> addedEntities;
  4. private Dictionary<IAggregateRoot, IUnitOfWorkRepository> changedEntities;
  5. private Dictionary<IAggregateRoot, IUnitOfWorkRepository> deletedEntities;
  6.  
  7. public NHUnitOfWork()
  8. {
  9. addedEntities = new Dictionary<IAggregateRoot, IUnitOfWorkRepository>();
  10. changedEntities = new Dictionary<IAggregateRoot, IUnitOfWorkRepository>();
  11. deletedEntities = new Dictionary<IAggregateRoot, IUnitOfWorkRepository>();
  12. }
  13.  
  14. public void RegisterUpdate(IAggregateRoot entity, IUnitOfWorkRepository unitofWorkRepository)
  15. {
  16. if (!changedEntities.ContainsKey(entity))
  17. {
  18. changedEntities.Add(entity, unitofWorkRepository);
  19. }
  20. }
  21.  
  22. public void RegisterAdd(IAggregateRoot entity, IUnitOfWorkRepository unitofWorkRepository)
  23. {
  24. if (!addedEntities.ContainsKey(entity))
  25. {
  26. addedEntities.Add(entity, unitofWorkRepository);
  27. };
  28. }
  29.  
  30. public void RegisterRemoved(IAggregateRoot entity, IUnitOfWorkRepository unitofWorkRepository)
  31. {
  32. if (!deletedEntities.ContainsKey(entity))
  33. {
  34. deletedEntities.Add(entity, unitofWorkRepository);
  35. }
  36. }
  37.  
  38. public void Commit()
  39. {
  40. using (TransactionScope scope = new TransactionScope())
  41. {
  42. foreach (IAggregateRoot entity in this.addedEntities.Keys)
  43. {
  44. this.addedEntities[entity].PersistCreationOf(entity);
  45. }
  46.  
  47. foreach (IAggregateRoot entity in this.changedEntities.Keys)
  48. {
  49. this.changedEntities[entity].PersistUpdateOf(entity);
  50. }
  51.  
  52. foreach (IAggregateRoot entity in this.deletedEntities.Keys)
  53. {
  54. this.deletedEntities[entity].PersistDeletionOf(entity);
  55. }
  56.  
  57. scope.Complete();
  58. }
  59. }

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

  1. public class AccountRepository : IAccountRepository,IUnitOfWorkRepository
  2. {
  3. private IUnitOfWork _unitOfWork;
  4.  
  5. public AccountRepository(IUnitOfWork unitOfWork)
  6. {
  7. _unitOfWork = unitOfWork;
  8. }
  9.  
  10. public void Save(Account account)
  11. {
  12. _unitOfWork.RegisterUpdate(account, this);
  13. }
  14.  
  15. public void Add(Account account)
  16. {
  17. _unitOfWork.RegisterAdd(account, this);
  18. }
  19.  
  20. public void Remove(Account account)
  21. {
  22. _unitOfWork.RegisterRemoved(account, this);
  23. }
  24.  
  25. public void PersistUpdateOf(IAggregateRoot entity)
  26. {
  27. // ADO.net code to update the entity...真正的SQL实现
  28. }
  29.  
  30. public void PersistCreationOf(IAggregateRoot entity)
  31. {
  32. // ADO.net code to Add the entity...真正的SQL实现
  33. }
  34.  
  35. public void PersistDeletionOf(IAggregateRoot entity)
  36. {
  37. // ADO.net code to delete the entity...真正的SQL实现
  38. }
  39. }

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

  UnitOfWork.Console:

  接下来,就是最后的控制台程序了。  

  1. class Program
  2. {
  3. static void Main(string[] args)
  4. {
  5. Account a = new Account(1000);
  6. System.Console.WriteLine("现在张三,存有{0}", a.Balance);
  7. Account b = new Account(200);
  8. System.Console.WriteLine("现在李四,存有{0}", b.Balance);
  9. System.Console.WriteLine("张三准备转500元给李四,转战开始了......");
  10.  
  11. //声明要使用的UnitOfWork
  12. IUnitOfWork nhUnitOfWork = new NHUnitOfWork();
  13.  
  14. //声明要使用的Repository
  15. IAccountRepository accountRepository = new AccountRepository(nhUnitOfWork);
  16.  
  17. AccountService service = new AccountService(accountRepository, nhUnitOfWork);
  18.  
  19. service.Transfer(a,b,500);
  20. System.Console.WriteLine("转账结束");
  21. System.Console.WriteLine("张三当前余额:{0}",a.Balance);
  22. System.Console.WriteLine("李四当前余额:{0}",b.Balance);
  23.  
  24. System.Console.ReadKey();
  25. }
  26. }
 

UnitOfWork实战的更多相关文章

  1. 【无私分享:ASP.NET CORE 项目实战(第五章)】Repository仓储 UnitofWork

    目录索引 [无私分享:ASP.NET CORE 项目实战]目录索引 简介 本章我们来创建仓储类Repository 并且引入 UnitOfWork 我对UnitOfWork的一些理解  UnitOfW ...

  2. 【asp.net core 系列】9 实战之 UnitOfWork以及自定义代码生成

    0. 前言 在前一篇中我们创建了一个基于EF的数据查询接口实现基类,这一篇我将带领大家讲一下为这EF补充一些功能,并且提供一个解决避免写大量配置类的方案. 1. SaveChanges的外移 在之前介 ...

  3. 【无私分享:ASP.NET CORE 项目实战】目录索引

    简介 首先,我们的  [无私分享:从入门到精通ASP.NET MVC]   系列已经接近尾声,希望大家在这个过程中学到了一些思路和方法,而不仅仅是源码. 因为是第一次写博客,我感觉还是比较混乱的,其中 ...

  4. 应用程序框架实战二十二 : DDD分层架构之仓储(层超类型基础篇)

    前一篇介绍了仓储的基本概念,并谈了我对仓储的一些认识,本文将实现仓储的基本功能. 仓储代表聚合在内存中的集合,所以仓储的接口需要模拟得像一个集合.仓储中有很多操作都是可以通用的,可以把这部分操作抽取到 ...

  5. Repository仓储 UnitofWork

    Repository仓储 UnitofWork 目录索引 [无私分享:ASP.NET CORE 项目实战]目录索引 简介 本章我们来创建仓储类Repository 并且引入 UnitOfWork 我对 ...

  6. LoadTest中内存和线程Troubleshooting实战

    LoadTest中内存和线程Troubleshooting实战 在端午节放假的三天中,我对正在开发的Service进行了LoadTest,尝试在增大压力的条件下发现问题. 该Service为独立进程的 ...

  7. 一步一步学EF系列 【7、结合IOC ,Repository,UnitOfWork来完成框架的搭建】

    前言 距离上一篇已经有段时间了,最近这段时间赶上新项目开发,一直没有时间来写.之前的几篇文章,主要把EF的基础都讲了一遍,这批文章就来个实战篇. 个人在学习过程中参考博客: Entity Framew ...

  8. 实战框架ABP

    abp及实战框架概述 接触abp也快一年了,有过大半年的abp项目开发经验,目前项目中所用的abp框架版本为0.10.3,最新的abp框架已经到了1.4,并且支持了asp.net core.关于abp ...

  9. 【asp.net core 系列】10 实战之ActionFilter

    0.前言 在上一篇中,我们提到了如何创建一个UnitOfWork并通过ActionFilter设置启用.这一篇我们将简单介绍一下ActionFilter以及如何利用ActionFilter,顺便补齐一 ...

随机推荐

  1. ubuntu server 11.10 mysql 自动备份脚本

    1.下载最新的备份脚本(AutoMySQLBackup) 点这里下载 2.修改脚本配置部分 vi  /root/automysqlbackup-2.5.1-01.sh USERNAME=root PA ...

  2. android 5.0新特性学习--视图轮廓

    ViewOutlineProvider -- 视图轮廓setOutlineProvider--可以使用View.setClipToOutline方法去剪切一个视图的outline区域.只有rectan ...

  3. 安装apk时出现错误Failure [INSTALL_FAILED_DEXOPT]问题解决的方法

    在android4.0源码里面编译出来apk后,用adb install (或adb install -r 重装)安装时,报错[INSTALL_FAILED_DEXOPT]. xu@xu-PC:~$  ...

  4. svg都快忘了,复习一下

    http://www.360doc.com/content/07/0906/21/39836_724430.shtml

  5. zf-关于通知公告显示问题

    1 公告结束日期超过当前时间是不能在通知公告上显示出来的 2 无限制时间的公告也是要在通知公告上显示出来的 于是我在后台实现类增加了如下代码 需要注意的是 当初解决第一个问题的时候增加了一个AND 当 ...

  6. jQuery常用及基础知识总结(三)

    1.通过jquery的$()引用元素包括通过id.class.元素名以及元素的层级关系及dom或者xpath条件等方法,且返回的对象为jquery对象(集合对象),不能直接调用dom定义的方法. 2. ...

  7. 实现免密码输入 ssh 登录

    实现免密码输入 ssh 登录假设 A 为客户机器, B 为目标机:要达到的目的:A 机器 ssh 登录 B 机器无需输入密码:加密方式选 rsa|dsa 均可以,默认 dsa做法:1.登录 A 机器2 ...

  8. HRBUST 1909 理工门外的树(双数组实现线段树功能)

    线段树固然可以做,但是在考虑线段树之前应该先考虑一下,这种用两个数组来解决问题的方法能不能行,如果行的话,就会为我们省很多时间,毕竟线段树代码量比较大 #include<iostream> ...

  9. 最完整的自动化测试流程:Python编写执行测试用例及定时自动发送最新测试报告邮件

    今天笔者就要归纳总结下一整套测试流程,从无到有,实现零突破,包括如何编写测试用例,定时执行测试用例,查找最新生成的测试报告文件,自动发送最新测试报告邮件,一整套完整的测试流程.以后各位只要着重如何编写 ...

  10. Android编程中的实用快捷键

    作为一个优秀的程序员,不但要能开发出漂亮的软件,也要能熟练掌握编程的技巧,包括IDE的快捷键使用.比如linux 下的VI编辑器,对于不熟练快捷键的人来说就是一个噩梦,但一旦你熟练了VI的快捷键,VI ...