工作单元(UnitOfWork) 模式 (1)
在开始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) 模式 (1)的更多相关文章
- .net core2.x - 关于工作单元(UnitOfWork) 模式
概要:在搭建框架,顺手说下写下,关于unitofwork,可能你理解了,可能你还不理解,可能与不可能不是重点,重点是感兴趣就看看吧. 1.工作单元(unitofowork)是什么(后面简写uow)? ...
- Asp.Net Core 工作单元 UnitOfWork UOW
Asp.Net Core 工作单元示例 来自 ABP UOW 去除所有无用特性 代码下载 : 去除所有无用特性版本,原生AspNetCore实现 差不多 2278 行代码: 链接:https://pa ...
- .NET Core 工作单元unitofwork 实现,基于NPOCO
现有项目中的orm 并非efcore,而是非主流的npoco,本身没有自带工作单元所以需要自己手撸一个,现记录一下,基于其他orm的工作单元照例实现应该没有什么问题 该实现基于NPOCO,针对其他的O ...
- MVC+EF 理解和实现仓储模式和工作单元模式
MVC+EF 理解和实现仓储模式和工作单元模式 原文:Understanding Repository and Unit of Work Pattern and Implementing Generi ...
- Asp.Net Core仓储模式+工作单元
仓储模式+工作单元 仓储模式 仓储(Repository)模式自2004年首次作为领域驱动模型DDD设计的一部分引入,仓储本质上是提供提供数据的抽象,以便应用程序可以使用具有接口的相似的简单抽象集合. ...
- EsayUI + MVC + ADO.NET(工作单元)
关联的设计 关联本身不是一个模式,但它在领域建模的过程中非常重要,所以需要在探讨各种模式之前,先讨论一下对象之间的关联该如何设计.我觉得对象的关联的设计可以遵循如下的一些原则: 关联尽量少,对象之间的 ...
- ABP框架 - 工作单元
文档目录 本节内容: 简介 在ABP中管理连接和事务 约定的工作单元 UnitOfWork 特性 IUnitOfWorkManager 工作单元详情 禁用工作单元 非事务性工作单元 工作单元方法调用另 ...
- ABP的工作单元
http://www.aspnetboilerplate.com/Pages/Documents/Unit-Of-Work 工作单元位于领域层. ABP的数据库连接和事务处理: 1,仓储类 ASP ...
- ABP官方文档翻译 3.6 工作单元
工作单元 介绍 ABP中的连接和事务管理 传统的工作单元方法 控制工作单元 UnitOfWork特性 IUnitOfWorkManager 工作单元详情 禁用工作单元 无事务工作单元 一个工作单元方法 ...
- 仓储(Repository)和工作单元模式(UnitOfWork)
仓储和工作单元模式 仓储模式 为什么要用仓储模式 通常不建议在业务逻辑层直接访问数据库.因为这样可能会导致如下结果: 重复的代码 编程错误的可能性更高 业务数据的弱类型 更难集中处理数据,比如缓存 无 ...
随机推荐
- 到今天了你还不会集合的Stream操作吗?你要out了
Java8的两个重大改变,一个是Lambda表达式,另一个就是本节要讲的Stream API表达式.Stream 是Java8中处理集合的关键抽象概念,它可以对集合进行非常复杂的查找.过滤.筛选等操作 ...
- PHP 中使用 ElasticSearch 的最佳实践 (下)
引言 上一篇文章,我们使用同步的方式将数据,同步写入到 ElasticSearch 中.接下来的这篇文章,主要介绍使用 RabbitMQ 的方式,异步的将数据同步到 ElasticSearch . 部 ...
- Android 13 - Media框架(3)- MediaPlayer生命周期
关注公众号免费阅读全文,进入音视频开发技术分享群! 上一节了解了MediaPlayer api的使用,这一节就我们将会了解MediaPlayer的生命周期与api使用细节. 1.MediaPlayer ...
- Objetive-C 属性和线程安全
一.接上一篇<nonatomic 带来的线程安全问题>,这里继续详细讨论属性各种类型与线程安全的关系 1)影响线程安全的属性类型,nonatomic,atomic,weak @proper ...
- python+k8s——基础练习
列表 core_api = client.CoreV1Api() # 管理核心资源(Pod, Service, ConfigMap 等) apps_api = client.AppsV1Api() # ...
- docker——存储配置与管理
docker存储配置与管理 查看docker info [root@hmm overlay2]# docker info Client: Docker Engine - Community Versi ...
- C语言——函数基本知识
什么是函数 函数的定义 函数就是程序中独立的功能.(简单来说就是把代码打包成整体,起个名字,方便以后使用) 函数的使用 定义函数的格式 格式: 返回值类型 函数名(形参1,形参2,...) { 函数体 ...
- Linux signal()函数
这是在网易云课堂学习Linux下的signal()函数的笔记,课程链接:https://study.163.com/course/courseLearn.htm?courseId=1002913011 ...
- Wakeup Source框架设计与实现
Wakeup Source 为系统组件提供了投票机制,以便低功耗子系统判断当前是否可以进入休眠. Wakeup Source(后简称:WS) 模块可与内核中的其他模块或者上层服务交互,并最终体现在对睡 ...
- WPS WORD EXCEL 不合并显示
WPS WORD EXCEL 不合并显示 版本:WPS 12 , 下载时间约是2023 年. 1.在开始菜单里找到 WPS OFFICE - 配置工具 2.点击"高级(A)". 3 ...