• .NET MVC4 实训记录之四(Unit of work + Repository)


      今日后开启进阶模式!

      谈到MVC与EntityFramework,则不得不说一说事务与仓储(Unit of work + Repository)。

      仓储(Repository):领域对象集合。用于操作领域对象与数据库上下文(DbContext)的交互(在此不得不说一声,领域对象和数据库表对象还是有区别的。领域对象实际上是一组有业务关系的数据库对象的抽象。最简单的形式就是主表、关系表在同一个领域对象中进行定义。例如我们前几章看到的UserProfile,它即定义了用户信息,又定义了用户角色关系信息)。

      事务(Transaction):多个业务处理有必然的顺序性、依赖性,则默认这些业务为一个原子操作。在这里我们使用工作单元,即Unit of work模式,来维护事务的原子性。

      首先,先让我们构建仓储接口。为了更好的使用EntityFramework的延迟加载,所有查询集合的接口我们均使用IQueryable接口类型作为返回值。

     1  1     /// <summary>
     2  2     /// 基础仓储类型接口
     3  3     /// 采用泛型类接口定义
     4  4     /// </summary>
     5  5     /// <typeparam name="T">泛型参数</typeparam>
     6  6     public interface IRepository<T> : IDisposable where T : class
     7  7     {
     8  8         /// <summary>
     9  9         /// 返回当前表的所有记录
    10 10         /// </summary>
    11 11         /// <returns>T</returns>
    12 12         IQueryable<T> Entries();
    13 13 
    14 14         /// <summary>
    15 15         /// 通过过滤条件进行查询
    16 16         /// </summary>
    17 17         /// <param name="predicate">过滤条件表达式</param>
    18 18         /// <returns>T</returns>
    19 19         IQueryable<T> Filter(Expression<Func<T, bool>> predicate);
    20 20 
    21 21         /// <summary>
    22 22         /// 通过过滤条件进行查询
    23 23         /// </summary>
    24 24         /// <param name="predicate">过滤条件表达式</param>
    25 25         /// <param name="includes">需贪婪加载的属性名称</param>
    26 26         /// <returns>IQueryable</returns>
    27 27         IQueryable<T> Filter(Expression<Func<T, bool>> predicate, params string[] includes);
    28 28 
    29 29         /// <summary>
    30 30         /// 是否存在满足表达式的记录
    31 31         /// </summary>
    32 32         /// <param name="predicate">过滤条件表达式</param>
    33 33         /// <returns>Boolean</returns>
    34 34         bool Contains(Expression<Func<T, bool>> predicate);
    35 35 
    36 36         /// <summary>
    37 37         /// 按照数据库主键查询特定的实例
    38 38         /// </summary>
    39 39         /// <param name="keys">主键列表</param>
    40 40         /// <returns>T</returns>
    41 41         T Single(params object[] keys);
    42 42 
    43 43         /// <summary>
    44 44         /// 按照指定表达式查询特定的实例
    45 45         /// </summary>
    46 46         /// <param name="predicate">过滤条件表达式</param>
    47 47         /// <returns>T</returns>
    48 48         T FirstOrDefault(Expression<Func<T, bool>> predicate);
    49 49 
    50 50         /// <summary>
    51 51         /// 插入一条记录
    52 52         /// </summary>
    53 53         /// <param name="t">新实例</param>
    54 54         /// <param name="submitImmediately">是否直接提交。默认false。</param>
    55 55         /// <returns>T</returns>
    56 56         T Create(T t, bool submitImmediately = false);
    57 57 
    58 58         /// <summary>
    59 59         /// 删除一行记录
    60 60         /// </summary>
    61 61         /// <param name="t">要删除的实例</param>
    62 62         /// <param name="submitImmediately">是否直接提交。默认false。</param>
    63 63         void Delete(T t, bool submitImmediately = false);
    64 64 
    65 65         /// <summary>
    66 66         /// 删除满足表达式的记录
    67 67         /// </summary>
    68 68         /// <param name="predicate">过滤条件表达式</param>
    69 69         /// <param name="submitImmediately">是否直接提交。默认false。</param>
    70 70         void Delete(Expression<Func<T, bool>> predicate, bool submitImmediately = false);
    71 71 
    72 72         /// <summary>
    73 73         /// 更新一条记录
    74 74         /// </summary>
    75 75         /// <param name="t">要更新的实例</param>
    76 76         /// <param name="submitImmediately">是否直接提交。默认false。</param>
    77 77         void Update(T t, bool submitImmediately = false);
    78 78 
    79 79         /// <summary>
    80 80         /// 获取当前实例的主键
    81 81         /// </summary>
    82 82         /// <param name="t">实例</param>
    83 83         /// <returns>Object</returns>
    84 84         object GetKeyValue(T t);
    85 85     }
    View Code

      接下来是实现这个接口,真正去处理数据查询和操作的时候了。

      1  1     /// <summary>
      2   2     /// 仓储类型定义
      3   3     /// 采用泛型类定义
      4   4     /// </summary>
      5   5     /// <typeparam name="T">泛型参数</typeparam>
      6   6     public class Repository<T> : IRepository<T> where T : class
      7   7     {
      8   8         /// <summary>
      9   9         /// 数据库上下文
     10  10         /// </summary>
     11  11         public UsersContext Context { get; private set; }
     12  12 
     13  13         /// <summary>
     14  14         /// 当前表记录集合
     15  15         /// </summary>
     16  16         protected DbSet<T> DbSet
     17  17         {
     18  18             get
     19  19             {
     20  20                 return Context.Set<T>();
     21  21             }
     22  22         }
     23  23 
     24  24         /// <summary>
     25  25         /// 构造器
     26  26         /// </summary>
     27  27         public Repository()
     28  28         {
     29  29             Context = new UsersContext();
     30  30         }
     31  31 
     32  32         /// <summary>
     33  33         /// 构造器
     34  34         /// </summary>
     35  35         /// <param name="connectionString">连接字符串(名称)</param>
     36  36         public Repository(string connectionString)
     37  37         {
     38  38             Context = new UsersContext(connectionString);
     39  39         }
     40  40 
     41  41         /// <summary>
     42  42         /// 构造器
     43  43         /// </summary>
     44  44         /// <param name="context">数据库上下文</param>
     45  45         public Repository(UsersContext context)
     46  46         {
     47  47             Context = context;
     48  48         }
     49  49         
     50  50         /// <summary>
     51  51         /// 析构器
     52  52         /// </summary>
     53  53         public void Dispose()
     54  54         {
     55  55             if (Context != null)
     56  56                 Context.Dispose();
     57  57         }
     58  58 
     59  59         /// <summary>
     60  60         /// 返回当前表的所有记录
     61  61         /// </summary>
     62  62         /// <returns></returns>
     63  63         public IQueryable<T> Entries()
     64  64         {
     65  65             return DbSet.AsQueryable();
     66  66         }
     67  67 
     68  68         /// <summary>
     69  69         /// 通过过滤条件进行查询
     70  70         /// </summary>
     71  71         /// <param name="predicate">过滤条件表达式</param>
     72  72         /// <returns>T</returns>
     73  73         public IQueryable<T> Filter(Expression<Func<T, bool>> predicate)
     74  74         {
     75  75             return DbSet.Where(predicate);
     76  76         }
     77  77 
     78  78         /// <summary>
     79  79         /// 通过过滤条件进行查询
     80  80         /// </summary>
     81  81         /// <param name="predicate">过滤条件表达式</param>
     82  82         /// <param name="includes">需贪婪加载的属性名称</param>
     83  83         /// <returns>IQueryable</returns>
     84  84         public IQueryable<T> Filter(Expression<Func<T, bool>> predicate, params string[] includes)
     85  85         {
     86  86             var query = DbSet.Where(predicate);
     87  87             if (includes != null)
     88  88             {
     89  89                 foreach (var item in includes)
     90  90                 {
     91  91                     query = query.Include(item);
     92  92                 }
     93  93             }
     94  94             return query;
     95  95         }
     96  96 
     97  97         /// <summary>
     98  98         /// 是否存在满足表达式的记录
     99  99         /// </summary>
    100 100         /// <param name="predicate">过滤条件表达式</param>
    101 101         /// <returns>Boolean</returns>
    102 102         public bool Contains(Expression<Func<T, bool>> predicate)
    103 103         {
    104 104             return DbSet.Count(predicate) > 0;
    105 105         }
    106 106 
    107 107         /// <summary>
    108 108         /// 按照数据库主键查询特定的实例
    109 109         /// </summary>
    110 110         /// <param name="keys">主键列表</param>
    111 111         /// <returns>T</returns>
    112 112         public T Single(params object[] keys)
    113 113         {
    114 114             return DbSet.Find(keys);
    115 115         }
    116 116 
    117 117         /// <summary>
    118 118         /// 按照指定表达式查询特定的实例
    119 119         /// </summary>
    120 120         /// <param name="predicate">过滤条件表达式</param>
    121 121         /// <returns>T</returns>
    122 122         public T FirstOrDefault(Expression<Func<T, bool>> predicate)
    123 123         {
    124 124             return DbSet.FirstOrDefault(predicate);
    125 125         }
    126 126 
    127 127         /// <summary>
    128 128         /// 插入一条记录
    129 129         /// </summary>
    130 130         /// <param name="t">新实例</param>
    131 131         /// <param name="submitImmediately">是否直接提交。默认false。</param>
    132 132         /// <returns>T</returns>
    133 133         public T Create(T t, bool submitImmediately = false)
    134 134         {
    135 135             var newEntry = DbSet.Add(t);
    136 136             if (submitImmediately)
    137 137                 Context.SaveChanges();
    138 138             return newEntry;
    139 139         }
    140 140 
    141 141         /// <summary>
    142 142         /// 删除一行记录
    143 143         /// </summary>
    144 144         /// <param name="t">要删除的实例</param>
    145 145         /// <param name="submitImmediately">是否直接提交。默认false。</param>
    146 146         public void Delete(T t, bool submitImmediately = false)
    147 147         {
    148 148             DbSet.Remove(t);
    149 149             if (submitImmediately)
    150 150                 Context.SaveChanges();
    151 151         }
    152 152 
    153 153         /// <summary>
    154 154         /// 删除满足表达式的记录
    155 155         /// </summary>
    156 156         /// <param name="predicate">过滤条件表达式</param>
    157 157         /// <param name="submitImmediately">是否直接提交。默认false。</param>
    158 158         public void Delete(Expression<Func<T, bool>> predicate, bool submitImmediately = false)
    159 159         {
    160 160             try
    161 161             {
    162 162                 Context.Configuration.AutoDetectChangesEnabled = false; //关闭数据库上下文的自动更新跟踪功能,可提高批量操作的性能
    163 163 
    164 164                 var objects = Filter(predicate);
    165 165                 foreach (var obj in objects)
    166 166                     DbSet.Remove(obj);
    167 167                 if (submitImmediately)
    168 168                     Context.SaveChanges();
    169 169             }
    170 170             finally
    171 171             {
    172 172                 Context.Configuration.AutoDetectChangesEnabled = true; //完成批量操作后,打开数据库上下文的自动更新跟踪功能
    173 173             }
    174 174         }
    175 175 
    176 176         /// <summary>
    177 177         /// 更新一条记录
    178 178         /// </summary>
    179 179         /// <param name="t">要更新的实例</param>
    180 180         /// <param name="submitImmediately">是否直接提交。默认false。</param>
    181 181         public void Update(T t, bool submitImmediately = false)
    182 182         {
    183 183             var key = GetKeyValue(t);
    184 184 
    185 185             var originalEntity = DbSet.Find(key);
    186 186 
    187 187             Context.Entry(originalEntity).CurrentValues.SetValues(t);
    188 188 
    189 189             if (submitImmediately)
    190 190                 Context.SaveChanges();
    191 191         }
    192 192 
    193 193         /// <summary>
    194 194         /// 获取当前实例的主键
    195 195         /// </summary>
    196 196         /// <param name="t">实例</param>
    197 197         /// <returns>Object</returns>
    198 198         public object GetKeyValue(T t)
    199 199         {
    200 200             var key =
    201 201                 typeof(T).GetProperties().FirstOrDefault(
    202 202                     p => p.GetCustomAttributes(typeof(System.ComponentModel.DataAnnotations.KeyAttribute), true).Length != 0);
    203 203             return (key != null) ? key.GetValue(t, null) : null;
    204 204         }
    205 205     }
    View Code

      仓储定义完成。它包含了基本的增、删、改、查功能。无需过多的修饰,作为仓储单元,它的任务就是这么四个操作而已。

      接下来就是我们的Unit of work的定义了。

      1 namespace Framework.Repositories
      2 {
      3     /// <summary>
      4     /// 工作单元接口定义
      5     /// </summary>
      6     public interface IUnitOfWork: IDisposable
      7     {
      8         /// <summary>
      9         /// 获取数据库上下文
     10         /// </summary>
     11         UsersContext DbContext { get; }
     12 
     13         /// <summary>
     14         /// 执行自定义SQL语句。
     15         /// 该方法提供了一个直接操作数据库表的实现。
     16         /// </summary>
     17         /// <param name="commandText">SQL语句</param>
     18         /// <param name="parameters">参数列表</param>
     19         /// <returns>Integer</returns>
     20         int ExecuteSqlCommand(string commandText, params object[] parameters);
     21         
     22         /// <summary>
     23         /// 提交事务
     24         /// </summary>
     25         /// <returns>Integer</returns>
     26         int Commit();
     27 
     28         /// <summary>
     29         /// 获取指定类型的仓储实例
     30         /// </summary>
     31         /// <typeparam name="T">泛型参数</typeparam>
     32         /// <returns>IRepository</returns>
     33         IRepository<T> Repositry<T>() where T : class;
     34     }
     35 
     36     /// <summary>
     37     /// 工作单元实现定义
     38     /// </summary>
     39     public class UnitOfWork : IUnitOfWork
     40     {
     41         /// <summary>
     42         /// 用户数据库上下文
     43         /// </summary>
     44         private UsersContext dbContext = null;
     45 
     46         /// <summary>
     47         /// 获取数据库上下文
     48         /// </summary>
     49         public UsersContext DbContext { get { return dbContext; } }
     50 
     51         /// <summary>
     52         /// 构造器
     53         /// </summary>
     54         public UnitOfWork()
     55         {
     56             dbContext = new UsersContext();
     57         }
     58 
     59         /// <summary>
     60         /// 构造器
     61         /// </summary>
     62         /// <param name="context">数据库上下文</param>
     63         public UnitOfWork(UsersContext context)
     64         {
     65             dbContext = context;
     66         }
     67 
     68         /// <summary>
     69         /// 析构器
     70         /// </summary>
     71         public void Dispose()
     72         {
     73             if (dbContext != null)
     74                 dbContext.Dispose();
     75             GC.SuppressFinalize(this);
     76         }
     77 
     78         /// <summary>
     79         /// 获取指定类型的仓储实例
     80         /// </summary>
     81         /// <typeparam name="T">泛型参数</typeparam>
     82         /// <returns>IRepository</returns>
     83         public IRepository<T> Repositry<T>() where T : class
     84         {
     85             return new Repository<T>(DbContext);
     86         }
     87 
     88         /// <summary>
     89         /// 提交事务
     90         /// </summary>
     91         /// <returns>Integer</returns>
     92         public int Commit()
     93         {
     94             return dbContext.SaveChanges();
     95         }
     96 
     97         /// <summary>
     98         /// 执行自定义SQL语句。
     99         /// 该方法提供了一个直接操作数据库表的实现。
    100         /// </summary>
    101         /// <param name="commandText">SQL语句</param>
    102         /// <param name="parameters">参数列表</param>
    103         /// <returns>Integer</returns>
    104         public int ExecuteSqlCommand(string commandText, params object[] parameters)
    105         {
    106             return dbContext.Database.ExecuteSqlCommand(commandText, parameters);
    107         }
    108     } 
    109 }
    View Code 

      OK,基础类型的定义已经完成,看看我们如何使用它吧。先模拟一个场景:当前要添加一个用户,同时在添加用户的时候,要吧该用户增加到指定的部门列表(UserDepartment)下。

     1         public void AddNewUser(string userName, string department)
     2         {
     3             using (IUnitOfWork unit = new UnitOfWork())
     4             {
     5                 //获取用户类型的仓储
     6                 var usrRep = unit.Repositry<UserProfile>();
     7                 //创建新用户
     8                 var User = new UserProfile { UserName = userName };
     9 
    10                 //将用户信息添加到数据库。
    11                 //注意:我们没有使用Create接口的第二个参数,即表示第二个参数默认为false,这表示当前操作暂时不提交到数据库。
    12                 //如果使用 usrRep.Create(User, true), 则表示直接提交当前记录到数据库。
    13                 //假如有兴趣,可以尝试第二个参数为true,执行该句之后人为抛出一个异常,看看数据库是如何发生变化的。
    14                 usrRep.Create(User);
    15                 
    16                 //throw new Exception("");
    17 
    18                 //获取部门类型的仓储
    19                 var depRep = unit.Repositry<Department>();
    20                 //根据部门名称获取部门信息
    21                 var Department = depRep.FirstOrDefault(p => p.Name.Equals(department));
    22                 //将当前用户添加到该部门内
    23                 Department.DepartmentUsers.Add(new UserDepartment { UserId = User.UserId, DepartmentId = Department.Id });
    24 
    25                 //提交前面所有的操作。这个才是最关键的。没有这句,一切都是瞎忙活!!!
    26                 unit.Commit();
    27             }
    28         }
    View Code

      当然,通常我们不会这么去做。我们会在UserProfile的定义中添加一个Department集合,同样会在Department中添加一个UserProfile集合,构建用户与部门的多对多关系(EntityFramework的多种数据映射关系以后我们会提到,网上相应的资料也很多),这样就可以很容易的只是用用户仓储实例进行操作,只需一个usrRep.Create(User, true)操作就可以完成上面的业务。不过我们只是为了说明Unitofwork如何工作,大家不必太较真。

      好了,今天就此结束。希望我能坚持不懈,也希望大家能一起见证我们努力的结果。

  • 相关阅读:
    软件工程第一次结对作业
    软件工程第二次作业
    vue之vuex
    ceshi
    第3次 结构与部分
    第二次作业
    获得领跑衫感言
    期末总结
    第十四,十五周作业
    第七周作业
  • 原文地址:https://www.cnblogs.com/libra1006/p/3898359.html
Copyright © 2020-2023  润新知