zoukankan      html  css  js  c++  java
  • 【.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

  • 相关阅读:
    Linkerd 2.10(Step by Step)—将 GitOps 与 Linkerd 和 Argo CD 结合使用
    Linkerd 2.10(Step by Step)—多集群通信
    Linkerd 2.10(Step by Step)—使用 Kustomize 自定义 Linkerd 的配置
    Linkerd 2.10(Step by Step)—控制平面调试端点
    Linkerd 2.10(Step by Step)—配置超时
    Linkerd 2.10(Step by Step)—配置重试
    Linkerd 2.10(Step by Step)—配置代理并发
    本地正常运行,线上环境诡异异常原因集合
    Need to invoke method 'xxx' declared on target class 'yyy', but not found in any interface(s) of the exposed proxy type
    alpine 安装常用命令
  • 原文地址:https://www.cnblogs.com/retop/p/5193394.html
Copyright © 2011-2022 走看看