.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如何工作,大家不必太较真。

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

你可能感兴趣的:(repository)