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();