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


    回顾

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

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

    理论介绍

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

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

    具体实践

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

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

     1 public interface IRepository<T>
     2     {
     3         /// <summary>
     4         /// 插入对象
     5         /// </summary>
     6         /// <param name="entity"></param>
     7         int Insert(T entity);
     8 
     9         /// <summary>
    10         /// 更新对象
    11         /// </summary>
    12         /// <param name="entity"></param>
    13         /// <param name="predicate"></param>
    14         int Update(T entity, Expression<Func<T, bool>> express);
    15 
    16         /// <summary>
    17         /// 删除对象
    18         /// </summary>
    19         /// <param name="predicate"></param>
    20         int Delete(Expression<Func<T, bool>> express = null);
    21 
    22         /// <summary>
    23         /// 查询对象集合
    24         /// </summary>
    25         /// <param name="predicate"></param>
    26         /// <returns></returns>
    27         List<T> QueryAll(Expression<Func<T, bool>> express = null);
    28 
    29         /// <summary>
    30         /// 查询对象集合
    31         /// </summary>
    32         /// <param name="index"></param>
    33         /// <param name="pagesize"></param>
    34         /// <param name="order"></param>
    35         /// <param name="asc"></param>
    36         /// <param name="express"></param>
    37         /// <returns></returns>
    38         List<T> QueryAll(int index,int pagesize,List<PropertySortCondition> orderFields, Expression<Func<T, bool>> express = null);
    39 
    40         /// <summary>
    41         /// 查询对象集合
    42         /// </summary>
    43         /// <param name="type"></param>
    44         /// <param name="predicate"></param>
    45         /// <returns></returns>
    46         List<object> QueryAll(Type type, Expression<Func<T, bool>> express = null);
    47 
    48         /// <summary>
    49         /// 查询对象
    50         /// </summary>
    51         /// <param name="predicate"></param>
    52         /// <returns></returns>
    53         T Query(Expression<Func<T, bool>> express);
    54 
    55         /// <summary>
    56         /// 查询数量
    57         /// </summary>
    58         /// <param name="predicate"></param>
    59         /// <returns></returns>
    60         object QueryCount(Expression<Func<T, bool>> express = null);
    61     }

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

      1  public abstract class BaseRepository<T> : IRepository<T>
      2         where T:class,new()
      3     {
      4         private IUnitOfWork unitOfWork;
      5 
      6         private IUnitOfWorkContext context;
      7 
      8         public BaseRepository(IUnitOfWork unitOfWork, IUnitOfWorkContext context)
      9         {
     10             this.unitOfWork = unitOfWork;
     11             this.context = context;
     12         }
     13 
     14         Lazy<ConditionBuilder> builder = new Lazy<ConditionBuilder>();
     15 
     16         public string tableName {
     17             get
     18             {
     19                 TableNameAttribute attr= (TableNameAttribute)typeof(T).GetCustomAttribute(typeof(TableNameAttribute));
     20                 return attr.Name; 
     21             }
     22         }
     23 
     24         /// <summary>
     25         /// 插入对象
     26         /// </summary>
     27         /// <param name="entity"></param>
     28         public virtual int Insert(T entity)
     29         {
     30             Func<PropertyInfo[], string, IDictionary<string, object>, int> excute = (propertys, condition, parameters) =>
     31             {
     32                 List<string> names = new List<string>();
     33                 foreach (PropertyInfo property in propertys)
     34                 {
     35                     if (property.GetCustomAttribute(typeof(IncrementAttribute)) == null)
     36                     {
     37                         string attrName = property.Name;
     38                         object value = property.GetValue(entity);
     39                         names.Add(string.Format("@{0}", attrName));
     40                         parameters.Add(attrName, value);
     41                     }
     42                 }
     43                 string sql = "Insert into {0} values({1})";
     44                 string combineSql = string.Format(sql, tableName, string.Join(",", names), builder.Value.Condition);
     45                 return unitOfWork.Command(combineSql, parameters);
     46             };
     47             return CreateExcute<int>(null, excute);
     48         }
     49 
     50         /// <summary>
     51         /// 修改对象
     52         /// </summary>
     53         /// <param name="entity"></param>
     54         /// <param name="express"></param>
     55         public virtual int Update(T entity, Expression<Func<T, bool>> express)
     56         {
     57 
     58             Func<PropertyInfo[], string, IDictionary<string, object>, int> excute = (propertys, condition, parameters) =>
     59             {
     60                 List<string> names = new List<string>();
     61                 foreach (PropertyInfo property in propertys)
     62                 {
     63                     if (property.GetCustomAttribute(typeof(IncrementAttribute)) == null)
     64                     {
     65                         string attrName = property.Name;
     66                         object value = property.GetValue(entity);
     67                         names.Add(string.Format("{0}=@{1}", attrName, attrName));
     68                         parameters.Add(attrName, value);
     69                     }
     70                 }
     71                 string sql = "update {0} set {1} where {2}";
     72                 string combineSql = string.Format(sql, tableName, string.Join(",", names), builder.Value.Condition);
     73                 return unitOfWork.Command(combineSql, parameters);
     74             };
     75             return CreateExcute<int>(express, excute);
     76         }
     77         /// <summary>
     78         /// 删除对象
     79         /// </summary>
     80         /// <param name="express"></param>
     81         public virtual int Delete(Expression<Func<T, bool>> express = null)
     82         {
     83             Func<PropertyInfo[], string, IDictionary<string, object>, int> excute = (propertys, condition, parameters) =>
     84             {
     85                 string sql = "delete from {0} {1}";
     86                 string combineSql = string.Format(sql, tableName, condition);
     87                 return unitOfWork.Command(combineSql, parameters);
     88             };
     89             return CreateExcute<int>(express, excute);
     90         }
     91 
     92         /// <summary>
     93         /// 查询对象集合
     94         /// </summary>
     95         /// <param name="express"></param>
     96         /// <returns></returns>
     97         public virtual List<T> QueryAll(Expression<Func<T, bool>> express = null)
     98         {
     99             Func<PropertyInfo[], string, IDictionary<string, object>, List<T>> excute = (propertys, condition, parameters) =>
    100             {
    101                 string sql = "select {0} from {1} {2}";
    102                 string combineSql = string.Format(sql, string.Join(",", propertys.Select(x => x.Name)), tableName, condition);
    103                 return context.ReadValues<T>(combineSql, parameters);
    104             };
    105             return CreateExcute<List<T>>(express, excute);
    106         }
    107 
    108         /// <summary>
    109         /// 查询对象集合(分页)
    110         /// </summary>
    111         /// <param name="index"></param>
    112         /// <param name="pagesize"></param>
    113         /// <param name="order"></param>
    114         /// <param name="asc"></param>
    115         /// <param name="express"></param>
    116         /// <returns></returns>
    117         public virtual List<T> QueryAll(int index,int pagesize,List<PropertySortCondition> orderFields,Expression<Func<T, bool>> express = null)
    118         {
    119             Func<PropertyInfo[], string, IDictionary<string, object>, List<T>> excute = (propertys, condition, parameters) =>
    120             {
    121                 if (orderFields == null) { throw new Exception("排序字段不能为空"); }
    122                 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}";
    123                 string combineSql = string.Format(sql, string.Join(",", propertys.Select(x => x.Name)),string.Join(",", orderFields), tableName, condition, (index - 1) * pagesize + 1, index * pagesize);
    124                 return context.ReadValues<T>(combineSql, parameters);
    125             };
    126             return CreateExcute<List<T>>(express, excute);
    127         }
    128 
    129         /// <summary>
    130         /// 查询对象集合
    131         /// </summary>
    132         /// <param name="type"></param>
    133         /// <param name="express"></param>
    134         /// <returns></returns>
    135         public virtual List<object> QueryAll(Type type, Expression<Func<T, bool>> express = null)
    136         {
    137             Func<PropertyInfo[], string, IDictionary<string, object>, List<object>> excute = (propertys, condition, parameters) =>
    138             {
    139                 string sql = "select {0} from {1} {2}";
    140                 string combineSql = string.Format(sql, string.Join(",", propertys.Select(x => x.Name)), tableName, condition);
    141                 return context.ReadValues(combineSql, type, parameters);
    142             };
    143             return CreateExcute<List<object>>(express, excute);
    144         }
    145 
    146         /// <summary>
    147         /// 查询对象
    148         /// </summary>
    149         /// <param name="express"></param>
    150         /// <returns></returns>
    151         public virtual T Query(Expression<Func<T, bool>> express)
    152         {
    153             Func<PropertyInfo[], string, IDictionary<string, object>, T> excute = (propertys, condition, parameters) =>
    154             {
    155                 string sql = "select {0} from {1} {2}";
    156                 string combineSql = string.Format(sql, string.Join(",", propertys.Select(x => x.Name)), tableName, condition);
    157                 return context.ExecuteReader<T>(combineSql, parameters);
    158             };
    159             return CreateExcute<T>(express, excute);
    160         }
    161 
    162         /// <summary>
    163         /// 查询数量
    164         /// </summary>
    165         /// <param name="express"></param>
    166         /// <returns></returns>
    167         public virtual object QueryCount(Expression<Func<T, bool>> express = null)
    168         {
    169             Func<PropertyInfo[], string, IDictionary<string, object>, object> excute = (propertys, condition, parameters) =>
    170             {
    171                 string sql = "select * from {0} {1}";
    172                 string combineSql = string.Format(sql, string.Join(",", propertys.Select(x => x.Name)), tableName, condition);
    173                 return context.ExecuteScalar(combineSql, parameters);
    174             };
    175 
    176             return CreateExcute<object>(express, excute);
    177         }
    178 
    179         private TValue CreateExcute<TValue>(Expression<Func<T, bool>> express, Func<PropertyInfo[], string, IDictionary<string, object>, TValue> excute)
    180         {
    181             Dictionary<string, object> parameters = new Dictionary<string, object>();
    182             PropertyInfo[] propertys = typeof(T).GetProperties();
    183             string condition = "";
    184             if (express != null)
    185             {
    186                 builder.Value.Build(express, tableName);
    187                 condition = string.Format("where {0} ", builder.Value.Condition);
    188                 for (int i = 0; i < builder.Value.Arguments.Length; i++)
    189                 {
    190                     parameters.Add(string.Format("Param{0}", i), builder.Value.Arguments[i]);
    191                 }
    192             }
    193             return excute(propertys, condition, parameters);
    194         }
    195     }

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

     1 public interface IUnitOfWork
     2     {
     3         /// <summary>
     4         /// 命令
     5         /// </summary>
     6         /// <param name="commandText"></param>
     7         /// <param name="parameters"></param>
     8         /// <returns></returns>
     9         int Command(string commandText, IDictionary<string, object> parameters);
    10 
    11         /// <summary>
    12         /// 事务的提交状态
    13         /// </summary>
    14         bool IsCommited { get; set; }
    15 
    16         /// <summary>
    17         /// 提交事务
    18         /// </summary>
    19         /// <returns></returns>
    20         void Commit();
    21 
    22         /// <summary>
    23         /// 回滚事务
    24         /// </summary>
    25         void RollBack();
    26     }

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

     1 public class UnitOfWork:IUnitOfWork
     2     {
     3         /// <summary>
     4         /// 注入对象
     5         /// </summary>
     6         private IUnitOfWorkContext context;
     7 
     8         /// <summary>
     9         /// 维护一个Sql语句的命令列表
    10         /// </summary>
    11         private List<CommandObject> commands;
    12 
    13         public UnitOfWork(IUnitOfWorkContext context)
    14         {
    15             commands = new List<CommandObject>();
    16             this.context = context;
    17         }
    18 
    19         /// <summary>
    20         /// 增、删、改命令 
    21         /// </summary>
    22         /// <param name="commandText"></param>
    23         /// <param name="parameters"></param>
    24         /// <returns></returns>
    25         public int Command(string commandText, IDictionary<string, object> parameters)
    26         {
    27             IsCommited = false;
    28             commands.Add(new CommandObject(commandText, parameters));
    29             return 1;
    30         }
    31 
    32         /// <summary>
    33         /// 提交状态
    34         /// </summary>
    35         public bool IsCommited{ get; set; }
    36 
    37         /// <summary>
    38         /// 提交方法
    39         /// </summary>
    40         /// <returns></returns>
    41         public void Commit()
    42         {
    43             if (IsCommited) { return ; }
    44             using (TransactionScope scope = new TransactionScope())
    45             {
    46                 foreach (var command in commands)
    47                 {
    48                     context.ExecuteNonQuery(command.command, command.parameters);
    49                 }
    50                 scope.Complete();
    51                 IsCommited = true;
    52             }
    53         }
    54 
    55         /// <summary>
    56         /// 事务回滚
    57         /// </summary>
    58         public void RollBack()
    59         {
    60             IsCommited = false;
    61         }
    62     }

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

     1 public interface IUnitOfWorkContext
     2     {
     3 
     4         /// <summary>
     5         /// 注册新对象到上下文
     6         /// </summary>
     7         /// <param name="commandText"></param>
     8         /// <param name="parameters"></param>
     9         int ExecuteNonQuery(string commandText, IDictionary<string, object> parameters = null);
    10 
    11         /// <summary>
    12         ///  查询对象集合
    13         /// </summary>
    14         /// <typeparam name="T"></typeparam>
    15         /// <param name="commandText"></param>
    16         /// <param name="parameters"></param>
    17         /// <param name="load">自定义处理</param>
    18         /// <returns></returns>
    19         List<T> ReadValues<T>(string commandText, IDictionary<string, object> parameters = null, Func<IDataReader, T> load = null) where T : class, new();
    20 
    21         /// <summary>
    22         /// 查询对象集合
    23         /// </summary>
    24         /// <param name="commandText"></param>
    25         /// <param name="type"></param>
    26         /// <param name="parameters"></param>
    27         /// <param name="setItem"></param>
    28         /// <returns></returns>
    29         List<object> ReadValues(string commandText, Type type, IDictionary<string, object> parameters = null, Action<dynamic> setItem = null);
    30 
    31         /// <summary>
    32         /// 查询对象
    33         /// </summary>
    34         /// <typeparam name="TEntity"></typeparam>
    35         /// <param name="commandText"></param>
    36         /// <param name="parameters"></param>
    37         /// <param name="excute"></param>
    38         /// <returns></returns>
    39         T ExecuteReader<T>(string commandText, IDictionary<string, object> parameters = null, Func<IDataReader, T> load = null) where T : class,new();
    40 
    41         /// <summary>
    42         /// 查询数量
    43         /// </summary>
    44         /// <param name="commandText"></param>
    45         /// <param name="parameters"></param>
    46         /// <returns></returns>
    47         object ExecuteScalar(string commandText, IDictionary<string, object> parameters = null);
    48     }

    最后实现。

      1 public abstract class UnitOfWorkContext : IUnitOfWorkContext,IDisposable
      2     {
      3         /// <summary>
      4         /// 数据库连接字符串标识
      5         /// </summary>
      6         public abstract string Key { get; }
      7 
      8         private SqlConnection connection;
      9 
     10         private SqlConnection Connection 
     11         {
     12             get 
     13             {
     14                 if (connection == null)
     15                 {
     16                     ConnectionStringSettings settings = ConfigurationManager.ConnectionStrings[Key];
     17                     connection = new SqlConnection(settings.ConnectionString);
     18                 }
     19                 return connection;
     20             }    
     21         }
     22 
     23         /// <summary>
     24         /// 注册新对象到事务
     25         /// </summary>
     26         /// <typeparam name="TEntity"></typeparam>
     27         /// <param name="entity"></param>
     28         public int ExecuteNonQuery(string commandText, IDictionary<string, object> parameters = null)
     29         {
     30             Func<SqlCommand, int> excute = (commend) =>
     31             {
     32                 return commend.ExecuteNonQuery();
     33             };
     34             return CreateDbCommondAndExcute<int>(commandText, parameters, excute);
     35         }
     36 
     37         /// <summary>
     38         /// 查询对象集合
     39         /// </summary>
     40         /// <typeparam name="T"></typeparam>
     41         /// <param name="commandText"></param>
     42         /// <param name="parameters"></param>
     43         /// <param name="load">自定义处理</param>
     44         /// <returns>泛型实体集合</returns>
     45 
     46         public List<T> ReadValues<T>(string commandText, IDictionary<string, object> parameters = null, Func<IDataReader, T> load = null) where T : class,new()
     47         {
     48             Func<SqlCommand, List<T>> excute = (dbCommand) =>
     49             {
     50                 List<T> result = new List<T>();
     51                 using (IDataReader reader = dbCommand.ExecuteReader())
     52                 {
     53                     while (reader.Read())
     54                     {
     55                         if (load == null)
     56                         {
     57                             load = (s) => { return s.GetReaderData<T>(); };
     58                         }
     59                         var item = load(reader);
     60                         result.Add(item);
     61                     }
     62                     return result;
     63                 }
     64             };
     65             return CreateDbCommondAndExcute(commandText, parameters, excute);
     66         }
     67 
     68         /// <summary>
     69         /// 查询对象集合
     70         /// </summary>
     71         /// <param name="commandText"></param>
     72         /// <param name="parameters"></param>
     73         /// <param name="setItem"></param>
     74         /// <returns></returns>
     75         public List<object> ReadValues(string commandText, Type type, IDictionary<string, object> parameters = null, Action<dynamic> setItem = null)
     76         {
     77             Func<SqlCommand, List<object>> excute = (dbCommand) =>
     78             {
     79                 var result = new List<object>();
     80 
     81                 using (IDataReader dataReader = dbCommand.ExecuteReader())
     82                 {
     83                     while (dataReader.Read())
     84                     {
     85                         var item = dataReader.GetReaderData(type);
     86                         if (setItem != null)
     87                         {
     88                             setItem(item);
     89                         }
     90                         result.Add(item);
     91                     }
     92                 }
     93                 return result;
     94             };
     95             return CreateDbCommondAndExcute<List<object>>(commandText, parameters,
     96                 excute);
     97         }
     98 
     99         /// <summary>
    100         /// 查询对象
    101         /// </summary>
    102         /// <typeparam name="TEntity"></typeparam>
    103         /// <param name="commandText"></param>
    104         /// <param name="parameters"></param>
    105         /// <param name="excute"></param>
    106         /// <returns></returns>
    107         public T ExecuteReader<T>(string commandText, IDictionary<string, object> parameters = null, Func<IDataReader, T> load = null) where T : class,new()
    108         {
    109             Func<SqlCommand, T> excute = (dbCommand) =>
    110             {
    111                 var result = default(T);
    112                 using (IDataReader reader = dbCommand.ExecuteReader())
    113                 {
    114                     while (reader.Read())
    115                     {
    116                         if (load == null)
    117                         {
    118                             load = (s) => { return s.GetReaderData<T>(); };
    119                         }
    120                         result = load(reader);
    121                     }
    122                     return result;
    123                 }
    124             };
    125             return CreateDbCommondAndExcute<T>(commandText, parameters, excute);
    126         }
    127 
    128         /// <summary>
    129         /// 查询数量
    130         /// </summary>
    131         /// <param name="commandText"></param>
    132         /// <param name="parameters"></param>
    133         /// <returns></returns>
    134         public object ExecuteScalar(string commandText, IDictionary<string, object> parameters = null)
    135         {
    136             Func<SqlCommand, object> excute = (dbCommand) =>
    137             {
    138                 return dbCommand.ExecuteScalar();
    139             };
    140             return CreateDbCommondAndExcute(commandText, parameters, excute);
    141         }
    142 
    143         /// <summary>
    144         /// 创建命令并执行
    145         /// </summary>
    146         /// <typeparam name="TValue"></typeparam>
    147         /// <param name="commandText"></param>
    148         /// <param name="parameters"></param>
    149         /// <param name="excute"></param>
    150         /// <returns></returns>
    151         private TValue CreateDbCommondAndExcute<TValue>(string commandText,
    152             IDictionary<string, object> parameters, Func<SqlCommand, TValue> excute)
    153         {
    154             if (Connection.State == ConnectionState.Closed) { Connection.Open(); };
    155             using (SqlCommand command = new SqlCommand())
    156             {
    157                 command.CommandType = CommandType.Text;
    158                 command.CommandText = commandText;;
    159                 command.Connection = Connection;
    160                 command.SetParameters(parameters);
    161                 return excute(command);
    162             }
    163         }
    164 
    165         /// <summary>
    166         /// 关闭连接
    167         /// </summary>
    168         public void Dispose()
    169         {
    170             if (connection != null)
    171             {
    172                 Connection.Dispose();//非托管资源
    173             }
    174         }
    175     }
    View Code

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

     1 public class Services : IService
     2     {
     3         private IMemberRespository member;
     4 
     5         private IUnitOfWork unitOfWork;
     6 
     7         public Services(IMemberRespository member, IUnitOfWork unitOfWork)
     8         {
     9             this.member = member;
    10             this.unitOfWork = unitOfWork;
    11         }
    12 
    13         /// <summary>
    14         /// 测试用例
    15         /// </summary>
    16         public void Demo()
    17         {
    18 
    19             member.Test();
    20 
    21             unitOfWork.Commit();
    22         }
    23     }

    后记

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

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

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

  • 相关阅读:
    USB描述符(转)
    (转)Linux设备驱动之HID驱动 源码分析
    Linux USB 鼠标驱动程序详解(转)
    (转)linux如何获取鼠标相对位置信息
    从零写一个编译器(四):语法分析之构造有限状态自动机
    从零写一个编译器(三):语法分析之几个基础数据结构
    从零写一个编译器(二):语法分析之前置知识
    从零写一个编译器(一):输入系统和词法分析
    自底向上语法分析
    递归下降和LL(1)语法分析
  • 原文地址:https://www.cnblogs.com/retop/p/5193394.html
Copyright © 2020-2023  润新知