工作单元 — Unit Of Work

在进行数据库添加、修改、删除时,为了保证事务的一致性,即操作要么全部成功,要么全部失败。例如银行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)


下一篇:linux配置备忘