简介html
最近忙着新项目的架构,已经有一段时间没有更新博客了,一直考虑着要写些什么,直到有一天跟朋友谈起他们公司开发数据层遇到的一些问题时,我想应该分享一些项目中使用的数据访问模式。web
虽然最近一直都在使用Go语言开发数据服务器,可是本篇文章用到的语言仍然是C#,文章内提供的代码仅仅是分享如何使用工做单元,至于如何将这个模式引入到项目中去,就须要各位本身去实现了,毕竟每一个项目都是不同的,须要根据项目具体的环境来进行组合。sql
本篇文章包括如下内容:数据库
什么是工做单元服务器
该模式用来维护一个由已经被业务事务修改(CRUD除了R)的业务对象组成的列表并负责协调这些修改的持久化工做以及全部标记的并发问题。架构
在web应用中,因为每一个用户的请求都是属于不一样线程的,须要保持每次请求的全部数据操做都成功的状况下提交数据,只要有一个失败的操做,则会对用户的这次请求的全部操做进行回滚,以确保用户操做的数据始终处于有效的状态。并发
须要更详细的了解,能够查看此篇文章。框架
基于ADO.NET的实现ui
在不使用任何数据层框架,仅仅使用ADO.NET的状况下,通常流程的方式以下:this
通常状况下,咱们会给每一个数据库表建立对应的数据库交互类并提供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框架的工做单元,那么文章就到这里了,若是有问题和疑问欢迎留言,代码仅供参考请勿应用到实际项目中,谢谢。