Unit Of Work--工作单元(一)
简介
最近忙着新项目的架构,已经有一段时间没有更新博客了,一直考虑着要写些什么,直到有一天跟朋友谈起他们公司开发数据层遇到的一些问题时,我想应该分享一些项目中使用的数据访问模式。
虽然最近一直都在使用Go语言开发数据服务器,但是本篇文章用到的语言仍然是C#,文章内提供的代码仅仅是分享如何使用工作单元,至于如何将这个模式引入到项目中去,就需要各位自己去实现了,毕竟每个项目都是不一样的,需要根据项目具体的环境来进行组合。
本篇文章包括以下内容:
- 什么是工作单元
- 基于ADO.NET的实现
- 总结
什么是工作单元
该模式用来维护一个由已经被业务事务修改(CRUD除了R)的业务对象组成的列表并负责协调这些修改的持久化工作以及所有标记的并发问题。
在web应用中,由于每个用户的请求都是属于不同线程的,需要保持每次请求的所有数据操作都成功的情况下提交数据,只要有一个失败的操作,则会对用户的此次请求的所有操作进行回滚,以确保用户操作的数据始终处于有效的状态。
需要更详细的了解,可以查看此篇文章。
基于ADO.NET的实现
在不使用任何数据层框架,仅仅使用ADO.NET的情况下,一般流程的方式如下:
- 实例化IDbConnection
- 然后实例化IDbCommand
- 设置IDbCommand的Text和Parameters
- 执行IDbCommand.ExecuteNoQuery
- 释放IDbCommand、IDbConnection
一般情况下,我们会给每个数据库表创建对应的数据库交互类并提供CRUD方法,除了R以外,其他的方法都会实现以上的流程。
然而如果用户一次请求会进行多次CUD操作的情况下,只能在用户开始进行数据操作之前使用TransactionScope将多次操作包裹在内,这样实现相对麻烦的。
其实我们可以将用户进行的CUD的SQL语句与参数现在保存起来,到最后再一并进行提交,有点类似存储过程的样子,这其实就是工作单元模式了,首先创建一个用来存储SQL语句和参数的类,代码如下:
class SQLEntity
{
private string m_SQL = null;
public string SQL
{
get { return m_SQL; }
} private IDataParameter[] m_Parameters = null;
public IDataParameter[] Parameters
{
get { return m_Parameters; }
} public Entity(string sql, params IDataParameter[] parameters)
{
this.m_SQL = sql;
this.m_Parameters = parameters;
}
}
如果将所有CUD的操作都放在一个List<SQLEntity>内,在需要提交的时候只需要遍历List<SQLEntity>内的元素,并通过重复开始的流程就行(这里需要稍微重构一下),代码如下:
class SQLUnitOfWork
{
private IDbConnection m_connection = null;
private List m_operations = new List(); public SQLUnitOfWork(IDbConnection connection)
{
this.m_connection = connection;
} public void RegisterAdd(string sql, params IDataParameter[] parameters)
{
this.m_operations.Add(new SQLEntity(sql, parameters));
} public void RegisterSave(string sql, params IDataParameter[] parameters)
{
this.m_operations.Add(new SQLEntity(sql, parameters));
} public void RegisterRemove(string sql, params IDataParameter[] parameters)
{
this.m_operations.Add(new SQLEntity(sql, parameters));
} public void Commit()
{
using (IDbTransaction trans = this.m_connection.BeginTransaction())
{
try
{
using (IDbCommand cmd = this.m_connection.CreateCommand())
{
cmd.Transaction = trans;
cmd.CommandType = CommandType.Text;
this.ExecuteQueryBy(cmd);
}
trans.Commit();
}
catch (Exception ex)
{
trans.Rollback();
}
}
} private void ExecuteQueryBy(IDbCommand cmd)
{
foreach (var entity in this.m_operations)
{
cmd.CommandText = entity.SQL;
cmd.Parameters.Clear();
foreach (var parameter in entity.Parameters)
{
cmd.Parameters.Add(parameter);
}
cmd.ExecuteNonQuery();
}
this.m_operations.Clear();
}
}
有了以上的SQLUnitOfWork,我们的数据库类在调用CUD方法的时候,就只要调用相应RegisterXXX方法了,数据层实现代码如下:
class SchoolRepository
{
private SQLUnitOfWork m_uow = null; public SchoolRepository(SQLUnitOfWork uow)
{
this.m_uow = uow;
} public void Add(School school)
{
this.m_uow.RegisterAdd("insert school values(@id, @name)", new IDbDataParameter[]{
new SqlParameter("@id", school.Id),
new SqlParameter("@name", school.Name)
});
} public void Save(School school)
{
this.m_uow.RegisterSave("update school set name = @name where id = @id", new IDbDataParameter[]{
new SqlParameter("@id", school.Id),
new SqlParameter("@name", school.Name)
});
} public void Remove(School school)
{
this.m_uow.RegisterRemove("delete from school where id = @id", new IDbDataParameter[]{
new SqlParameter("id", school.Id)
});
}
} class StudentRepository
{
//代码类似,因此省略
}
有了以上的准备,再使用以下的代码来看看工作单元的效果,代码如下:
SQLUnitOfWork uow = new SQLUnitOfWork(connection);
SchoolRepository schoolRepositry = new SchoolRepository(uow);
StudentRepository studentRepository = new StudentRepository(uow); School school = new School
{
Id = Guid.NewGuid().ToString(),
Name = "一中",
};
schoolRepositry.Add(school); for (int i = 0; i < 7; i++)
{
Student student = new Student
{
Id = Guid.NewGuid().ToString(),
Name = string.Format("学生{0}号", i),
Age = 7 + i,
SchoolId = school.Id
};
studentRepository.Add(student);
} school.Name = "二中";
schoolRepositry.Save(school); uow.Commit();
接着会看到数据库中会看到图中的数据,如图:
总结
这样就完成了基于ADO.NET的简单工作单元实现了,可能有些人会有疑问,跟其他人实现的工作单元有很多不同的地方,这是因为该版本只是一个大致思路的实现,并没有跟其他框架、库的结合,为了能使其他人理解工作单元的原理,因此实现相对比较简单。
由于模式是一种解决方案,一种思路,每个项目的环境、功能、结构都不一样,因此实现的方式会有不同。对于模式不能死记硬背,要理解其中的原理,可以通过自我实践或者参考他人的代码来实现,如果每个项目都是照抄模式的话,那么就失去了它该有的作用了。
在下一篇文章中,我会重构以上的代码,实现以兼容ADO.NET和ORM框架的工作单元,那么文章就到这里了,如果有问题和疑问欢迎留言,代码仅供参考请勿应用到实际项目中,谢谢。
Unit Of Work--工作单元(一)的更多相关文章
- ABP(现代ASP.NET样板开发框架)系列之12、ABP领域层——工作单元(Unit Of work)
点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之12.ABP领域层——工作单元(Unit Of work) ABP是“ASP.NET Boilerplate Pr ...
- ABP领域层——工作单元(Unit Of work)
ABP领域层——工作单元(Unit Of work) 点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之12.ABP领域层——工作单元(Unit Of work) ...
- 解析ABP框架中的事务处理和工作单元,ABP事务处理
通用连接和事务管理方法连接和事务管理是使用数据库的应用程序最重要的概念之一.当你开启一个数据库连接,什么时候开始事务,如何释放连接...诸如此类的. 正如大家都知道的,.Net使用连接池(connec ...
- [Abp 源码分析]六、工作单元的实现
0.简介 在 Abp 框架内部实现了工作单元,在这里讲解一下,什么是工作单元? Unit Of Work(工作单元)模式用来维护一个由已经被业务事物修改(增加.删除或更新)的业务对象组成的列表.Uni ...
- [.NET领域驱动设计实战系列]专题四:前期准备之工作单元模式(Unit Of Work)
一.前言 在前一专题中介绍了规约模式的实现,然后在仓储实现中,经常会涉及工作单元模式的实现.然而,在我的网上书店案例中也将引入工作单元模式,所以本专题将详细介绍下该模式,为后面案例的实现做一个铺垫. ...
- 换个角度说工作单元(Unit Of Work):创建、持有与API调用
看到一些工作单元的介绍,有两种感觉,第一种是很学院,说了等于没说,我估计很多都是没有自己引入到实际的项目中去,第二种是告诉我一种结果,说这就是工作单元,但是没说为什么要这么使用.所以,本篇想要探讨的是 ...
- 工作单元(Unit of Work)
维护受业务事务影响的对象列表,并协调变化的写入和并发问题的解决. 从DB中存取Data时,必须记录增删改动作,以将对DB有影响的数据写会到DB中去. 如果在每次修改对象模型时就对DB进行相应的修改,会 ...
- ABP理论学习之工作单元(Unit of Work)
返回总目录 本篇目录 公共连接和事务管理方法 ABP中的连接和事务管理 仓储类 应用服务 工作单元 工作单元详解 关闭工作单元 非事务的工作单元 工作单元方法调用其它 工作单元作用域 自动保存 IRe ...
- 基于DDD的.NET开发框架 - ABP工作单元(Unit of Work)
返回ABP系列 ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)”的简称. ASP.NET Boilerplate是一个用最佳实践和流行技术开发现代WEB应 ...
- 【.Net设计模式系列】工作单元(Unit Of Work)模式 ( 二 )
回顾 在上一篇博客[.Net设计模式系列]仓储(Repository)模式 ( 一 ) 中,通过各位兄台的评论中,可以看出在设计上还有很多的问题,在这里特别感谢 @横竖都溢 @ 浮云飞梦 2位兄台对博 ...
随机推荐
- TaskCompletionSource的使用场景
TaskCompletionSource生成Task的另一种方法.使用TaskCompletionSource很简单,只需要实例化它即可.TaskCompletionSource有一个Task属性,你 ...
- C#与数据库访问技术总结(十四)之DataAdapter对象
DataAdapter对象 DataAdapter对象主要用来承接Connection和DataSet对象. DataSet对象只关心访问操作数据,而不关心自身包含的数据信息来自哪个Connectio ...
- 深入理解requestAnimationFrame
前言 本文主要参考w3c资料,从底层实现原理的角度介绍了requestAnimationFrame.cancelAnimationFrame,给出了相关的示例代码以及我对实现原理的理解和讨论. 先来看 ...
- 深入了解javascript事件流
摘要:事件流这个东西是比较重要的,为了让自己更加理解js中的事件流,必须整理整理,梳理一下事件流的各种东西啊.本文大部分内容参考<javascript高级程序设计第三版> 先来一段书里的原 ...
- 常用linux命令索引
每天一个linux命令(61):wget命令 每天一个linux命令(60):scp命令 每天一个linux命令(59):rcp命令 每天一个linux命令(58):telnet命令 每天一个linu ...
- vs emulator for android使用
在windows上调试android程序,可以利用hyperv虚拟化功能,微软也提供了模拟工具和android studio.eclipse的配置说明,不再累述. 关于启动vs模拟器的cmd命令: e ...
- Android Application 对象介绍
What is Application Application和Actovotu,Service一样是android框架的一个系统组件,当android程序启动时系统会创建一个 application ...
- 淘宝开放平台TOP测试环境
沙箱测试环境 淘宝沙箱环境是淘宝开放平台(TOP)提供给独立软件开发商(ISV)的测试环境.数据完全独立,大部分API已经部署到该环境中供ISV进行API的功能测试,对与APP的调用量无限制,但获取大 ...
- android: SQLite更新数据
学习完了如何向表中添加数据,接下来我们看看怎样才能修改表中已有的数据. SQLiteDatabase 中也是提供了一个非常好用的 update()方法用于对数据进行更新,这个方法 接收四个参数,第一个 ...
- maven eclipse miss required library解决
我是直接到C:\Users\admin\.m2\repository目录把所有的库包全删除,然后在项目里刷新一下,搞定!