简介

  最近忙着新项目的架构,已经有一段时间没有更新博客了,一直考虑着要写些什么,直到有一天跟朋友谈起他们公司开发数据层遇到的一些问题时,我想应该分享一些项目中使用的数据访问模式。

  虽然最近一直都在使用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--工作单元(一)的更多相关文章

  1. ABP(现代ASP.NET样板开发框架)系列之12、ABP领域层——工作单元(Unit Of work)

    点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之12.ABP领域层——工作单元(Unit Of work) ABP是“ASP.NET Boilerplate Pr ...

  2. ABP领域层——工作单元(Unit Of work)

    ABP领域层——工作单元(Unit Of work) 点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之12.ABP领域层——工作单元(Unit Of work) ...

  3. 解析ABP框架中的事务处理和工作单元,ABP事务处理

    通用连接和事务管理方法连接和事务管理是使用数据库的应用程序最重要的概念之一.当你开启一个数据库连接,什么时候开始事务,如何释放连接...诸如此类的. 正如大家都知道的,.Net使用连接池(connec ...

  4. [Abp 源码分析]六、工作单元的实现

    0.简介 在 Abp 框架内部实现了工作单元,在这里讲解一下,什么是工作单元? Unit Of Work(工作单元)模式用来维护一个由已经被业务事物修改(增加.删除或更新)的业务对象组成的列表.Uni ...

  5. [.NET领域驱动设计实战系列]专题四:前期准备之工作单元模式(Unit Of Work)

    一.前言 在前一专题中介绍了规约模式的实现,然后在仓储实现中,经常会涉及工作单元模式的实现.然而,在我的网上书店案例中也将引入工作单元模式,所以本专题将详细介绍下该模式,为后面案例的实现做一个铺垫. ...

  6. 换个角度说工作单元(Unit Of Work):创建、持有与API调用

    看到一些工作单元的介绍,有两种感觉,第一种是很学院,说了等于没说,我估计很多都是没有自己引入到实际的项目中去,第二种是告诉我一种结果,说这就是工作单元,但是没说为什么要这么使用.所以,本篇想要探讨的是 ...

  7. 工作单元(Unit of Work)

    维护受业务事务影响的对象列表,并协调变化的写入和并发问题的解决. 从DB中存取Data时,必须记录增删改动作,以将对DB有影响的数据写会到DB中去. 如果在每次修改对象模型时就对DB进行相应的修改,会 ...

  8. ABP理论学习之工作单元(Unit of Work)

    返回总目录 本篇目录 公共连接和事务管理方法 ABP中的连接和事务管理 仓储类 应用服务 工作单元 工作单元详解 关闭工作单元 非事务的工作单元 工作单元方法调用其它 工作单元作用域 自动保存 IRe ...

  9. 基于DDD的.NET开发框架 - ABP工作单元(Unit of Work)

    返回ABP系列 ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)”的简称. ASP.NET Boilerplate是一个用最佳实践和流行技术开发现代WEB应 ...

  10. 【.Net设计模式系列】工作单元(Unit Of Work)模式 ( 二 )

    回顾 在上一篇博客[.Net设计模式系列]仓储(Repository)模式 ( 一 ) 中,通过各位兄台的评论中,可以看出在设计上还有很多的问题,在这里特别感谢 @横竖都溢 @ 浮云飞梦 2位兄台对博 ...

随机推荐

  1. Null 与 “” 的区别

    说明:很多人有时候对于 null 和 "" 不是很清楚,结合其他人的文章,今天做下解释. String str1 = null; str引用为空 String str2 = &qu ...

  2. Spirng quartz 整合

    以下是资料来源: quartz maven confighttp://quartz-scheduler.org/downloads Spring 定时器(Timer,Quartz)http://sup ...

  3. wicket基础应用(1)--使用wicket对表单中的数据进行验证

    作者:lhx1026 出处:http://lhx1026.iteye.com/ wicket基础应用(1)--使用wicket对表单中的数据进行验证 举个例子: 1.有一个Java文件SysCharg ...

  4. Web端测试和移动端测试的区别

    1.记录bug 在Web端可以通过系统自带的截图和QQ截图等方式来截取bug的图片,对于错误的地方可以用工具自带的标识来重点标记. 对于移动端设备可以用手机自带的截图工具来截图然后传到电脑上,个人一般 ...

  5. Java 线程 — JMM Java内存模型

    JMM Java Memory Model,Java内存模型,属于语言级的内存模型 并发编程中存在的问题: 如何通信:用于线程之间交换信息.两种方式:共享内存,消息传递 如何同步:用于控制不同线程间操 ...

  6. 大家一起写mvc(三)_结束

    上一篇介绍到要写mvc的所用的核心技术,这一篇我们就开始真正的开始写mvc,其实就是把昨天写过的代码进行一些组装就可以了. 我们用eclipse新建一个web项目.然后web.xml如下 <?x ...

  7. HTML之总结

    基础结构 基础标签有HTML,head,body. 合格的的HTML页面: <!DOCTYPE html> <html lang="en"> <hea ...

  8. HTML 邮件链接,超链接发邮件

    在网页中可以设置如“联系我们”.“问题反馈”等所谓的邮箱链接,类似网页超链接,只是可以直接打开默认邮箱程序. 使用<a href="mailto:youEMail@xxx.yyy&qu ...

  9. GPRM/GNRMC定位信息的读取与解析

    帧头 UTC时间 状态 纬度 北纬/南纬 经度 东经/西经 速度 $GPRMC hhmmss.sss A/V ddmm.mmmm N/S dddmm.mmmm E/W 节 方位角 UTC日期 磁偏角 ...

  10. MongoDB副本集配置系列九:MongoDB 常见问题

    What is a namespace in MongoDB? If you remove a document, does MongoDB remove it from disk? When doe ...