在进行数据库添加、修改、删除时,为了保证事务的一致性,即操作要么全部成功,要么全部失败。例如银行A、B两个账户的转账业务。一方失败都会导致事务的不完整性,从而事务回滚。而工作单元模式可以跟踪事务,在操作完成时对事务进行统一提交。

具体实践

首先,讲解下设计思想:领域层通过相应的库实现泛型仓储接口来持久化聚合类,之后在抽象库中将对泛型仓储接口提供基础实现,并将对应的实体转化为SQl语句。这样领域层就不需要操作Sql语句即可完成CRUD操作,同时使用工作单元对事务进行统一提交。

1)定义仓储接口,包含基本的CRUD操作及其重载不同的查询

public interface IRepository<T>
{
/// <summary>
/// 插入对象
/// </summary>
/// <param name="entity"></param>
int Insert(T entity); /// <summary>
/// 更新对象
/// </summary>
/// <param name="entity"></param>
/// <param name="predicate"></param>
int Update(T entity, Expression<Func<T, bool>> express); /// <summary>
/// 删除对象
/// </summary>
/// <param name="predicate"></param>
int Delete(Expression<Func<T, bool>> express = null); /// <summary>
/// 查询对象集合
/// </summary>
/// <param name="predicate"></param>
/// <returns></returns>
List<T> QueryAll(Expression<Func<T, bool>> express = null); /// <summary>
/// 查询对象集合
/// </summary>
/// <param name="index"></param>
/// <param name="pagesize"></param>
/// <param name="order"></param>
/// <param name="asc"></param>
/// <param name="express"></param>
/// <returns></returns>
List<T> QueryAll(int index,int pagesize,List<PropertySortCondition> orderFields, Expression<Func<T, bool>> express = null); /// <summary>
/// 查询对象集合
/// </summary>
/// <param name="type"></param>
/// <param name="predicate"></param>
/// <returns></returns>
List<object> QueryAll(Type type, Expression<Func<T, bool>> express = null); /// <summary>
/// 查询对象
/// </summary>
/// <param name="predicate"></param>
/// <returns></returns>
T Query(Expression<Func<T, bool>> express); /// <summary>
/// 查询数量
/// </summary>
/// <param name="predicate"></param>
/// <returns></returns>
object QueryCount(Expression<Func<T, bool>> express = null);
}

其次,对仓储接口提供基本实现,这里由于使用了Lambda表达式,所以就需要进行表达式树的解析(这里我希望园友能自己去研究)。

public abstract class BaseRepository<T> : IRepository<T>
where T:class,new()
{
private IUnitOfWork unitOfWork; private IUnitOfWorkContext context; public BaseRepository(IUnitOfWork unitOfWork, IUnitOfWorkContext context)
{
this.unitOfWork = unitOfWork;
this.context = context;
} Lazy<ConditionBuilder> builder = new Lazy<ConditionBuilder>(); public string tableName {
get
{
TableNameAttribute attr= (TableNameAttribute)typeof(T).GetCustomAttribute(typeof(TableNameAttribute));
return attr.Name;
}
} /// <summary>
/// 插入对象
/// </summary>
/// <param name="entity"></param>
public virtual int Insert(T entity)
{
Func<PropertyInfo[], string, IDictionary<string, object>, int> excute = (propertys, condition, parameters) =>
{
List<string> names = new List<string>();
foreach (PropertyInfo property in propertys)
{
if (property.GetCustomAttribute(typeof(IncrementAttribute)) == null)
{
string attrName = property.Name;
object value = property.GetValue(entity);
names.Add(string.Format("@{0}", attrName));
parameters.Add(attrName, value);
}
}
string sql = "Insert into {0} values({1})";
string combineSql = string.Format(sql, tableName, string.Join(",", names), builder.Value.Condition);
return unitOfWork.Command(combineSql, parameters);
};
return CreateExcute<int>(null, excute);
} /// <summary>
/// 修改对象
/// </summary>
/// <param name="entity"></param>
/// <param name="express"></param>
public virtual int Update(T entity, Expression<Func<T, bool>> express)
{ Func<PropertyInfo[], string, IDictionary<string, object>, int> excute = (propertys, condition, parameters) =>
{
List<string> names = new List<string>();
foreach (PropertyInfo property in propertys)
{
if (property.GetCustomAttribute(typeof(IncrementAttribute)) == null)
{
string attrName = property.Name;
object value = property.GetValue(entity);
names.Add(string.Format("{0}=@{1}", attrName, attrName));
parameters.Add(attrName, value);
}
}
string sql = "update {0} set {1} where {2}";
string combineSql = string.Format(sql, tableName, string.Join(",", names), builder.Value.Condition);
return unitOfWork.Command(combineSql, parameters);
};
return CreateExcute<int>(express, excute);
}
/// <summary>
/// 删除对象
/// </summary>
/// <param name="express"></param>
public virtual int Delete(Expression<Func<T, bool>> express = null)
{
Func<PropertyInfo[], string, IDictionary<string, object>, int> excute = (propertys, condition, parameters) =>
{
string sql = "delete from {0} {1}";
string combineSql = string.Format(sql, tableName, condition);
return unitOfWork.Command(combineSql, parameters);
};
return CreateExcute<int>(express, excute);
} /// <summary>
/// 查询对象集合
/// </summary>
/// <param name="express"></param>
/// <returns></returns>
public virtual List<T> QueryAll(Expression<Func<T, bool>> express = null)
{
Func<PropertyInfo[], string, IDictionary<string, object>, List<T>> excute = (propertys, condition, parameters) =>
{
string sql = "select {0} from {1} {2}";
string combineSql = string.Format(sql, string.Join(",", propertys.Select(x => x.Name)), tableName, condition);
return context.ReadValues<T>(combineSql, parameters);
};
return CreateExcute<List<T>>(express, excute);
} /// <summary>
/// 查询对象集合(分页)
/// </summary>
/// <param name="index"></param>
/// <param name="pagesize"></param>
/// <param name="order"></param>
/// <param name="asc"></param>
/// <param name="express"></param>
/// <returns></returns>
public virtual List<T> QueryAll(int index,int pagesize,List<PropertySortCondition> orderFields,Expression<Func<T, bool>> express = null)
{
Func<PropertyInfo[], string, IDictionary<string, object>, List<T>> excute = (propertys, condition, parameters) =>
{
if (orderFields == null) { throw new Exception("排序字段不能为空"); }
string sql = "select * from (select {0} , ROW_NUMBER() over(order by {1}) as rownum from {2} {3}) as t where t.rownum >= {4} and t.rownum < {5}";
string combineSql = string.Format(sql, string.Join(",", propertys.Select(x => x.Name)),string.Join(",", orderFields), tableName, condition, (index - ) * pagesize + , index * pagesize);
return context.ReadValues<T>(combineSql, parameters);
};
return CreateExcute<List<T>>(express, excute);
} /// <summary>
/// 查询对象集合
/// </summary>
/// <param name="type"></param>
/// <param name="express"></param>
/// <returns></returns>
public virtual List<object> QueryAll(Type type, Expression<Func<T, bool>> express = null)
{
Func<PropertyInfo[], string, IDictionary<string, object>, List<object>> excute = (propertys, condition, parameters) =>
{
string sql = "select {0} from {1} {2}";
string combineSql = string.Format(sql, string.Join(",", propertys.Select(x => x.Name)), tableName, condition);
return context.ReadValues(combineSql, type, parameters);
};
return CreateExcute<List<object>>(express, excute);
} /// <summary>
/// 查询对象
/// </summary>
/// <param name="express"></param>
/// <returns></returns>
public virtual T Query(Expression<Func<T, bool>> express)
{
Func<PropertyInfo[], string, IDictionary<string, object>, T> excute = (propertys, condition, parameters) =>
{
string sql = "select {0} from {1} {2}";
string combineSql = string.Format(sql, string.Join(",", propertys.Select(x => x.Name)), tableName, condition);
return context.ExecuteReader<T>(combineSql, parameters);
};
return CreateExcute<T>(express, excute);
} /// <summary>
/// 查询数量
/// </summary>
/// <param name="express"></param>
/// <returns></returns>
public virtual object QueryCount(Expression<Func<T, bool>> express = null)
{
Func<PropertyInfo[], string, IDictionary<string, object>, object> excute = (propertys, condition, parameters) =>
{
string sql = "select * from {0} {1}";
string combineSql = string.Format(sql, string.Join(",", propertys.Select(x => x.Name)), tableName, condition);
return context.ExecuteScalar(combineSql, parameters);
}; return CreateExcute<object>(express, excute);
} private TValue CreateExcute<TValue>(Expression<Func<T, bool>> express, Func<PropertyInfo[], string, IDictionary<string, object>, TValue> excute)
{
Dictionary<string, object> parameters = new Dictionary<string, object>();
PropertyInfo[] propertys = typeof(T).GetProperties();
string condition = "";
if (express != null)
{
builder.Value.Build(express, tableName);
condition = string.Format("where {0} ", builder.Value.Condition);
for (int i = ; i < builder.Value.Arguments.Length; i++)
{
parameters.Add(string.Format("Param{0}", i), builder.Value.Arguments[i]);
}
}
return excute(propertys, condition, parameters);
}
}

接下来,定义工作单元,所有的添加、删除、修改操作都会被储存到工作单元中。

public interface IUnitOfWork
{
/// <summary>
/// 命令
/// </summary>
/// <param name="commandText"></param>
/// <param name="parameters"></param>
/// <returns></returns>
int Command(string commandText, IDictionary<string, object> parameters); /// <summary>
/// 事务的提交状态
/// </summary>
bool IsCommited { get; set; } /// <summary>
/// 提交事务
/// </summary>
/// <returns></returns>
void Commit(); /// <summary>
/// 回滚事务
/// </summary>
void RollBack();
}

接下来是对工作单元的实现,其内部维护了一个命令集合,存储Sql语句。

public class UnitOfWork:IUnitOfWork
{
/// <summary>
/// 注入对象
/// </summary>
private IUnitOfWorkContext context; /// <summary>
/// 维护一个Sql语句的命令列表
/// </summary>
private List<CommandObject> commands; public UnitOfWork(IUnitOfWorkContext context)
{
commands = new List<CommandObject>();
this.context = context;
} /// <summary>
/// 增、删、改命令
/// </summary>
/// <param name="commandText"></param>
/// <param name="parameters"></param>
/// <returns></returns>
public int Command(string commandText, IDictionary<string, object> parameters)
{
IsCommited = false;
commands.Add(new CommandObject(commandText, parameters));
return ;
} /// <summary>
/// 提交状态
/// </summary>
public bool IsCommited{ get; set; } /// <summary>
/// 提交方法
/// </summary>
/// <returns></returns>
public void Commit()
{
if (IsCommited) { return ; }
using (TransactionScope scope = new TransactionScope())
{
foreach (var command in commands)
{
context.ExecuteNonQuery(command.command, command.parameters);
}
scope.Complete();
IsCommited = true;
}
} /// <summary>
/// 事务回滚
/// </summary>
public void RollBack()
{
IsCommited = false;
}
}

最后定义工作单元对事务提交处理的上下文及其实现,同仓储模式中的代码。

public interface IUnitOfWorkContext
{ /// <summary>
/// 注册新对象到上下文
/// </summary>
/// <param name="commandText"></param>
/// <param name="parameters"></param>
int ExecuteNonQuery(string commandText, IDictionary<string, object> parameters = null); /// <summary>
/// 查询对象集合
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="commandText"></param>
/// <param name="parameters"></param>
/// <param name="load">自定义处理</param>
/// <returns></returns>
List<T> ReadValues<T>(string commandText, IDictionary<string, object> parameters = null, Func<IDataReader, T> load = null) where T : class, new(); /// <summary>
/// 查询对象集合
/// </summary>
/// <param name="commandText"></param>
/// <param name="type"></param>
/// <param name="parameters"></param>
/// <param name="setItem"></param>
/// <returns></returns>
List<object> ReadValues(string commandText, Type type, IDictionary<string, object> parameters = null, Action<dynamic> setItem = null); /// <summary>
/// 查询对象
/// </summary>
/// <typeparam name="TEntity"></typeparam>
/// <param name="commandText"></param>
/// <param name="parameters"></param>
/// <param name="excute"></param>
/// <returns></returns>
T ExecuteReader<T>(string commandText, IDictionary<string, object> parameters = null, Func<IDataReader, T> load = null) where T : class,new(); /// <summary>
/// 查询数量
/// </summary>
/// <param name="commandText"></param>
/// <param name="parameters"></param>
/// <returns></returns>
object ExecuteScalar(string commandText, IDictionary<string, object> parameters = null);
}

最后实现。

public abstract class UnitOfWorkContext : IUnitOfWorkContext,IDisposable
{
/// <summary>
/// 数据库连接字符串标识
/// </summary>
public abstract string Key { get; } private SqlConnection connection; private SqlConnection Connection
{
get
{
if (connection == null)
{
ConnectionStringSettings settings = ConfigurationManager.ConnectionStrings[Key];
connection = new SqlConnection(settings.ConnectionString);
}
return connection;
}
} /// <summary>
/// 注册新对象到事务
/// </summary>
/// <typeparam name="TEntity"></typeparam>
/// <param name="entity"></param>
public int ExecuteNonQuery(string commandText, IDictionary<string, object> parameters = null)
{
Func<SqlCommand, int> excute = (commend) =>
{
return commend.ExecuteNonQuery();
};
return CreateDbCommondAndExcute<int>(commandText, parameters, excute);
} /// <summary>
/// 查询对象集合
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="commandText"></param>
/// <param name="parameters"></param>
/// <param name="load">自定义处理</param>
/// <returns>泛型实体集合</returns> public List<T> ReadValues<T>(string commandText, IDictionary<string, object> parameters = null, Func<IDataReader, T> load = null) where T : class,new()
{
Func<SqlCommand, List<T>> excute = (dbCommand) =>
{
List<T> result = new List<T>();
using (IDataReader reader = dbCommand.ExecuteReader())
{
while (reader.Read())
{
if (load == null)
{
load = (s) => { return s.GetReaderData<T>(); };
}
var item = load(reader);
result.Add(item);
}
return result;
}
};
return CreateDbCommondAndExcute(commandText, parameters, excute);
} /// <summary>
/// 查询对象集合
/// </summary>
/// <param name="commandText"></param>
/// <param name="parameters"></param>
/// <param name="setItem"></param>
/// <returns></returns>
public List<object> ReadValues(string commandText, Type type, IDictionary<string, object> parameters = null, Action<dynamic> setItem = null)
{
Func<SqlCommand, List<object>> excute = (dbCommand) =>
{
var result = new List<object>(); using (IDataReader dataReader = dbCommand.ExecuteReader())
{
while (dataReader.Read())
{
var item = dataReader.GetReaderData(type);
if (setItem != null)
{
setItem(item);
}
result.Add(item);
}
}
return result;
};
return CreateDbCommondAndExcute<List<object>>(commandText, parameters,
excute);
} /// <summary>
/// 查询对象
/// </summary>
/// <typeparam name="TEntity"></typeparam>
/// <param name="commandText"></param>
/// <param name="parameters"></param>
/// <param name="excute"></param>
/// <returns></returns>
public T ExecuteReader<T>(string commandText, IDictionary<string, object> parameters = null, Func<IDataReader, T> load = null) where T : class,new()
{
Func<SqlCommand, T> excute = (dbCommand) =>
{
var result = default(T);
using (IDataReader reader = dbCommand.ExecuteReader())
{
while (reader.Read())
{
if (load == null)
{
load = (s) => { return s.GetReaderData<T>(); };
}
result = load(reader);
}
return result;
}
};
return CreateDbCommondAndExcute<T>(commandText, parameters, excute);
} /// <summary>
/// 查询数量
/// </summary>
/// <param name="commandText"></param>
/// <param name="parameters"></param>
/// <returns></returns>
public object ExecuteScalar(string commandText, IDictionary<string, object> parameters = null)
{
Func<SqlCommand, object> excute = (dbCommand) =>
{
return dbCommand.ExecuteScalar();
};
return CreateDbCommondAndExcute(commandText, parameters, excute);
} /// <summary>
/// 创建命令并执行
/// </summary>
/// <typeparam name="TValue"></typeparam>
/// <param name="commandText"></param>
/// <param name="parameters"></param>
/// <param name="excute"></param>
/// <returns></returns>
private TValue CreateDbCommondAndExcute<TValue>(string commandText,
IDictionary<string, object> parameters, Func<SqlCommand, TValue> excute)
{
if (Connection.State == ConnectionState.Closed) { Connection.Open(); };
using (SqlCommand command = new SqlCommand())
{
command.CommandType = CommandType.Text;
command.CommandText = commandText;;
command.Connection = Connection;
command.SetParameters(parameters);
return excute(command);
}
} /// <summary>
/// 关闭连接
/// </summary>
public void Dispose()
{
if (connection != null)
{
Connection.Dispose();//非托管资源
}
}
}

在调用方法时需要注意,一个事务涉及多个聚合时,需要保证传递同一工作单元,并在方法的最后调用Commit() 方法。

public class Services : IService
{
private IMemberRespository member; private IUnitOfWork unitOfWork; public Services(IMemberRespository member, IUnitOfWork unitOfWork)
{
this.member = member;
this.unitOfWork = unitOfWork;
} /// <summary>
/// 测试用例
/// </summary>
public void Demo()
{ member.Test(); unitOfWork.Commit();
}
}

工作单元 — Unit Of Work的更多相关文章

  1. 工作单元(Unit of Work)

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

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

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

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

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

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

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

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

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

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

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

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

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

  8. ABP领域层知识回顾之---工作单元

    1. 前言   在上一篇博文中(http://www.cnblogs.com/xiyin/p/6842958.html) 我们讲到了ABP领域层的仓储.这边博文我们来讲 工作单元.个人觉得比较重要.文 ...

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

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

随机推荐

  1. linux安装puppeteer

    1.安装 下载淘宝镜像的,可以同时下载puppeteer和chromium下面两条语句即可 npm install -g cnpm --registry=https://registry.npm.ta ...

  2. Leetcode题目338:比特位计数(中等)

    题目描述: 给定一个非负整数 num.对于 0 ≤ i ≤ num 范围中的每个数字 i ,计算其二进制数中的 1 的数目并将它们作为数组返回. 示例 1: 输入: 2 输出: [0,1,1] 示例  ...

  3. java Date 转mysql timestamp 秒数不一致

    mysql的字段类型是timestamp(0), java的类型的是util.Date, 在插入数据的时候发现, 数据库的实际数据秒数比预想的数据偶尔会大1秒. 问题的原因: mysql的timest ...

  4. Android 自定义AlertDialog退出对话框

    Android 自定义AlertDialog退出对话框 转 https://blog.csdn.net/wkh11/article/details/53081634在项目中很多时候会出现点击返回键出现 ...

  5. shell脚本中用到的计算

    在shell脚本中计算一般会涉及到let.$(()).$[].bc(另扩展expr).其中let.$(()).$[]都是用来做基本整数运算,bc可以用来做浮点运算. (1).let.$(()).$[] ...

  6. React Native npm第三方报错

    添加npm install第三方报错: 使用: yarn add 组件名称 注意: 这里不需要像npm的--save

  7. RESTful架构(Representational State Transfer资源表现层状态转换)

    1. 什么是REST REST全称是Representational State Transfer,中文意思是表述(编者注:通常译为表征)性状态转移. 它首次出现在2000年Roy Fielding的 ...

  8. Css3 伪元素

    伪元素---用于向某些选择器设置特殊的效果 语法:元素::伪元素 (element::pseudo-element) 兼容性:IE9+ FireFox4+ Chrome Safari Opera fi ...

  9. git clone、 remote、fetch、pull、push、remote

    git clone命令笔记 作用:远程克隆版本库 1. 克隆版本库 git clone <版本库的网址> git clone zoran@192.168.2.167:/data/gitda ...

  10. Unity 实现橡皮擦效果

    我所实现的橡皮擦效果是设置图片某点的像素的透明度为0,来简单实现擦除效果的: 下面是效果 首先需要注意两点:1:设置 Main Camera 的 projection 属性为Orthographic ...