简介

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

  虽然最近一直都在使用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. macd综合版

    参数设置  SHORE 12    LONG 26    MID 9 DIF:EMA(CLOSE,SHORT)-EMA(CLOSE,LONG); DEA:EMA(DIF,MID),COLOR88888 ...

  2. Seo的几个境界

    Seo的境界 第一层,弄些关键词排名上去. 是的,大部分人理解的Seoer,就到此为止 这里有技巧若干若干.很值得一些人去卖弄. 第二层,大量广泛的收录,很好的pr值 恭喜您,把握搜索长尾, 这种不显 ...

  3. Redis 环境搭建与使用(C#)

    Redis Redis是一个开源的使用ANSI C语言编写.支持网络.可基于内存亦可持久化的日志型.Key-Value数据库,并提供多种语言的API. Redis是一个key-value存储系统.和M ...

  4. 使用Maven编译项目遇到——“maven编码gbk的不可映射字符”解决办法 ——转载

    一.问题描述 今天在MyEclipse中使用Maven编译项目源代码时,结果如下了如下的错误

  5. javascript设计模式与开发实践阅读笔记(7)——迭代器模式

    迭代器模式:指提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示. 迭代器模式可以把迭代的过程从业务逻辑中分离出来,在使用迭代器模式之后,即使不关心对象的内部构造,也可以按顺 ...

  6. php-fpm的配置和优化

    php-fpm的安装目录 下面是我的平时的环境搭建php的各种安装目录,大家的基本也差不多. centos等linux平台 /usr/local/php/php /usr/local/php/etc/ ...

  7. Revit API 楼板开洞

    start [Transaction(TransactionMode.Manual)] [Regeneration(RegenerationOption.Manual)] , , ) *  / , - ...

  8. BigDecimal除法运算出现java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result的解决办法

    BigDecimal除法运算出现java.lang.ArithmeticException: Non-terminating decimal expansion; no exact represent ...

  9. Scala 深入浅出实战经典 第68讲:Scala并发编程原生线程Actor、Cass Class下的消息传递和偏函数实战解析

    王家林亲授<DT大数据梦工厂>大数据实战视频 Scala 深入浅出实战经典(1-87讲)完整视频.PPT.代码下载: 百度云盘:http://pan.baidu.com/s/1c0noOt ...

  10. Navi.Soft30.产品.代码生成器.操作手册

    1系统简介 1.1功能简述 在Net软件开发过程中,大部分时间都是在编写代码,并且都是重复和冗杂的代码.比如:要实现在数据库中10个表的增删改查功能,大部分代码都是相同的,只需修改10%的代码量.此时 ...