回顾

在上一篇博客【.Net设计模式系列】仓储(Repository)模式 ( 一 ) 中,通过各位兄台的评论中,可以看出在设计上还有很多的问题,在这里特别感谢 @横竖都溢 @ 浮云飞梦 2位兄台对博文中存在的问题给予指出,并提供出好的解决方案,同时也感谢其他园友的支持。欢迎各位园友对博文中出现的错误或者是设计误区给予指出,一方面防止“误人子弟”,另一方面则可以让大家共同成长。

对于上一篇博客,只是给大家提供了一种对于小型项目数据访问层的一种实现方式,通过Sql语句和传递参数来实现CRUD。并未达到真正意义上的解耦。特此在本篇继续完善。

理论介绍

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

理论参考:http://martinfowler.com/eaaCatalog/unitOfWork.html

具体实践

首先,讲解下设计思想:领域层通过相应的库实现泛型仓储接口来持久化聚合类,之后在抽象库中将对泛型仓储接口提供基础实现,并将对应的实体转化为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();
}
}

后记

该实现中并未实现对多表进行的联合查询,使用Lambda的方式去多表查询,有点自己写一个ORM的性质,由于Lz能力有限,顾有需求的园友可以自行扩展或者使用ORM,若有实现自行扩展的园友,望指教。

至此,既实现对数据访问层和领域层解耦,如果园友对我的比较认可,欢迎尝试去使用,在使用中遇到什么问题或有什么好的意见,也希望及时反馈给我。若某些园友不太认可我的设计,也希望批评指出。

源码网盘地址:链接:http://pan.baidu.com/s/1hqXJ3GK 密码:o0he

【.Net设计模式系列】工作单元(Unit Of Work)模式 ( 二 )的更多相关文章

  1. Java 设计模式系列(十八)备忘录模式(Memento)

    Java 设计模式系列(十八)备忘录模式(Memento) 备忘录模式又叫做快照模式(Snapshot Pattern)或Token模式,是对象的行为模式.备忘录对象是一个用来存储另外一个对象内部状态 ...

  2. Java 设计模式系列(十五)迭代器模式(Iterator)

    Java 设计模式系列(十五)迭代器模式(Iterator) 迭代器模式又叫游标(Cursor)模式,是对象的行为模式.迭代子模式可以顺序地访问一个聚集中的元素而不必暴露聚集的内部表象(interna ...

  3. Java 设计模式系列(十四)命令模式(Command)

    Java 设计模式系列(十四)命令模式(Command) 命令模式把一个请求或者操作封装到一个对象中.命令模式允许系统使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复 ...

  4. Java 设计模式系列(十七)中介者模式

    Java 设计模式系列(十七)中介者模式 用一个中介对象来封装一系列的对象交互.中介者使得各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立的改变它们之间的交互 一.中介者模式结构 Media ...

  5. Java 设计模式系列(十一)享元模式

    Java 设计模式系列(十一)享元模式 Flyweight 享元模式是对象的结构模式.享元模式以共享的方式高效地支持大量的细粒度对象. 一.享元模式的结构 享元模式采用一个共享来避免大量拥有相同内容对 ...

  6. Java 设计模式系列(八)装饰者模式

    Java 设计模式系列(八)装饰者模式 装饰模式又名包装(Wrapper)模式.装饰模式以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案.Decorator 或 Wrapper 一.装饰模 ...

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

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

  8. 工作单元(Unit of Work)

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

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

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

随机推荐

  1. 文件类型分类:头文件dirent.h中定义的文件类型与linux内文件符号对应关系

    头文件 dirent.h 定义了文件类型: enum{    DT_UNKNOWN = 0,         //未知类型    DT_FIFO = 1,            //first in, ...

  2. 代码审计:covercms 1.6

    小菜只能找找没人用的cms练练手了 cnvd上有个 CoverCMS V1.16存在多个漏洞 漏洞描述 :CoverCMS V1.16存在重装.信息泄露.暴力破解.存储型跨站脚本和反射型跨站脚本漏洞. ...

  3. Matlab脚本和函数

    脚本和函数 脚本: 特点:按照文件中所输入的指令执行,一段matlab指令集合.运行后,运算过程产生的所有变量保存在基本工作区.可以进行图形输出,如plot()函数. 举例: 脚本文件ex4_15.m ...

  4. 3.asp.net core 关键概念

    1. StartUp类 在Startup.ConfigureServices方法里配置或注册服务 在Startup.Configure方法里配置请求处理管道.请求处理管道由一系列中间件组建构成,每个中 ...

  5. 将netcore网站部署到docker容器中

    一.背景 最近一直在看docker的教程,基础知识看的差不多了.理论总要运用于实践,所以下面我们就来把最简单的一个netcore网站托管到docker容器中. 环境:1.docker for wind ...

  6. 记一次靠谱的nvm在window系统的安装

    最近电脑系统重装,电脑的环境配置也清空掉了,今天想写点代码,突然发现什么环境也没了,又要重新配置了. 安装node环境的话,完全可以直接去node的官网下载安装包安装,也不是特别复杂,但是由于之前跑的 ...

  7. Vue指令之事件修饰符

    事件修饰符: + .stop 阻止冒泡 + .prevent 阻止默认事件 + .capture 添加事件侦听器时使用事件捕获模式 + .self 只当事件在该元素本身(比如不是子元素)触发时触发回调 ...

  8. find 查找文件的命令

    find顾名思义就是查找,Linux下find命令提供相当多的查找条件,可以在众多文件或目录下查找你想要的任何文件或目录. 语法: find filename 我当前目录下有aaa.txt和bbb.t ...

  9. springboot集成rabbitmq并手动注册容器实现单个queue的ack模式

    原文:https://blog.csdn.net/qq_38439885/article/details/88982373 进入正题,本文会介绍两种实现rabbitmq的ack模式的方法,分别为: 一 ...

  10. 助教培训总结——熟练掌握GitHub及Git的使用方法

    一.Git 命令的理解和使用 1.使用git前需要建立一个本地仓库,用Git GUI Here的话就可以直接选择Create New Repository.Git Bash Here输入 命令git ...