(Unit of work + Repository)

今日后开启进阶模式!

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

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

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

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

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

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

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

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

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

  
namespace Framework.Repositories
{
/// <summary>
/// 工作单元接口定义
/// </summary>
public interface IUnitOfWork: IDisposable
{
/// <summary>
/// 获取数据库上下文
/// </summary>
UsersContext DbContext { get; }
/// <summary>
/// 执行自定义SQL语句。
/// 该方法提供了一个直接操作数据库表的实现。
/// </summary>
/// <param name="commandText">SQL语句</param>
/// <param name="parameters">参数列表</param>
/// <returns>Integer</returns>
int ExecuteSqlCommand(string commandText, params object[] parameters); /// <summary>
/// 提交事务
/// </summary>
/// <returns>Integer</returns>
int Commit();
/// <summary>
/// 获取指定类型的仓储实例
/// </summary>
/// <typeparam name="T">泛型参数</typeparam>
/// <returns>IRepository</returns>
IRepository<T> Repositry<T>() where T : class;
}
/// <summary>
/// 工作单元实现定义
/// </summary>
public class UnitOfWork : IUnitOfWork
{
/// <summary>
/// 用户数据库上下文
/// </summary>
private UsersContext dbContext = null;
/// <summary>
/// 获取数据库上下文
/// </summary>
public UsersContext DbContext { get { return dbContext; } }
/// <summary>
/// 构造器
/// </summary>
public UnitOfWork()
{
dbContext = new UsersContext();
}
/// <summary>
/// 构造器
/// </summary>
/// <param name="context">数据库上下文</param>
public UnitOfWork(UsersContext context)
{
dbContext = context;
}
/// <summary>
/// 析构器
/// </summary>
public void Dispose()
{
if (dbContext != null)
dbContext.Dispose();
GC.SuppressFinalize(this);
}
/// <summary>
/// 获取指定类型的仓储实例
/// </summary>
/// <typeparam name="T">泛型参数</typeparam>
/// <returns>IRepository</returns>
public IRepository<T> Repositry<T>() where T : class
{
return new Repository<T>(DbContext);
}
/// <summary>
/// 提交事务
/// </summary>
/// <returns>Integer</returns>
public int Commit()
{
return dbContext.SaveChanges();
}
/// <summary>
/// 执行自定义SQL语句。
/// 该方法提供了一个直接操作数据库表的实现。
/// </summary>
/// <param name="commandText">SQL语句</param>
/// <param name="parameters">参数列表</param>
/// <returns>Integer</returns>
public int ExecuteSqlCommand(string commandText, params object[] parameters)
{
return dbContext.Database.ExecuteSqlCommand(commandText, parameters);
}
}
}

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

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

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

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

 

Unit of work + Repository的更多相关文章

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

    今日后开启进阶模式! 谈到MVC与EntityFramework,则不得不说一说事务与仓储(Unit of work + Repository). 仓储(Repository):领域对象集合.用于操作 ...

  2. 关于EF Unit of Work Repository的简单用法

    其实ef本身就是unit of work+repository的 其中继承自DbContext的类就是unit of work context中的DbSet<T>属性就是repositor ...

  3. [转]Upgrading to Async with Entity Framework, MVC, OData AsyncEntitySetController, Kendo UI, Glimpse & Generic Unit of Work Repository Framework v2.0

    本文转自:http://www.tuicool.com/articles/BBVr6z Thanks to everyone for allowing us to give back to the . ...

  4. TinyFrame升级之七:重构Repository和Unit Of Work

    首先,重构的想法来源于以下文章:Correct use of Repository and Unit Of Work patterns in ASP.NET MVC,因为我发现在我的框架中,对Unit ...

  5. Using the Repository and Unit Of Work Pattern in .net core

    A typical software application will invariably need to access some kind of data store in order to ca ...

  6. 关于Repository、IUnitOfWork 在领域层和应用服务层之间的代码分布与实现

    本来早就准备总结一下关于Repository.IUnitOfWork之间的联系以及在各层中的分布,直到看到田园里的蟋蟀发表的文章:<DDD 领域驱动设计-谈谈 Repository.IUnitO ...

  7. Repository、IUnitOfWork

    Repository.IUnitOfWork 在领域层和应用服务层之间的代码分布与实现 本来早就准备总结一下关于Repository.IUnitOfWork之间的联系以及在各层中的分布,直到看到田园里 ...

  8. LCLFramework架构必须要知道的知识

    技术实现: 代码是否面向对象,要看你的继承怎么用 用抽象来展现层次感 用接口来制定操作的统一性 依赖住入(Inversion of Control) Unit of Work Repository D ...

  9. MVC3+EF4.1学习系列(十)----MVC+EF处理树形结构

    通过前几篇文章 我们处理了 一对一, 一对多,多对多关系 很好的发挥了ORM框架的做用 但是 少说了一种 树形结构的处理, 而这种树形关系 我们也经常遇到,常见的N级类别的处理, 以及经常有数据与类别 ...

随机推荐

  1. Android学习路线(十一)管理Activity的生命周期

    当一个用户进入.退出,再次进入你的应用时,你的应用中的Activity 会在它的生命周期的各个状态下切换. 比如,当你的activity第一次启动.它出如今系统的前方接受用户的焦点.在这个过程中,An ...

  2. HDU 1712 ACboy needs your help(包背包)

    HDU 1712 ACboy needs your help(包背包) pid=1712">http://acm.hdu.edu.cn/showproblem.php? pid=171 ...

  3. BestCoder-Round#33

    写在前面 这是我第一次做BestCoder, 熟悉的外观BestCoder模式. BC上不仅能看到英文, 背部Chinese view是中文题目 交的次数是会影响得分的. 所以有了把握再交. 至少例子 ...

  4. Cocos2d-X采用CCScrollView创建滚动视图

    CCScrollView滚动视图可以让游戏有效果,并能够通过滚动视图切换游戏场景,滚动视图通常用来选择在游戏中的级别 实例1:使用CCScrollView创建一个简单的滚动视图 首先创建一个Scrol ...

  5. DirectSound应用

    只是使用的假设PlaySound()这个API函数来显示的声音效果,然后,然后,它不会出现在混合声音,因为PlaySound还有播放期间声音,这将不可避免地导致现有声音停止. 因此,使用 PlaySo ...

  6. hdu 游乐场

    Problem Description   小时候,因为家里经济困难,小明从未去过游乐场,所以直到现在,他还心存遗憾.  最近,杭州刚建了一座游乐场,为了弥补儿时的遗憾,小明带了一笔钱迫不及待地要去体 ...

  7. 开源搜索引擎Iveely 0.8.0

    开源搜索引擎Iveely 0.8.0 这是一篇博客,不是,这是一篇开源人的心酸和喜悦,没有人可以理解我们的心情,一路的辛酸一路的艰辛,不过还好,在大家的支持下,总算是终见天日,谢谢那些给予我们无私帮助 ...

  8. 从头开始学JavaScript (十三)——Date类型

    说明:UTC:国际协调日期 GMT:格林尼治标准时间 一.date类型的创建 使用new操作符和Date()构造函数 var now=new Date(): 传入参数:Date.parse()和Dat ...

  9. iOS8自适应布局视频教程

    联系:http://www.elsyy.com/course/6480 这是颐和园最近录制iOS8视频课程.简介iOS8出现在自适应布局. 本教程的书,颐和园<ios8 swift编程指南> ...

  10. hibernate 一对多关联关系(具体分析)

    在领域模型中, 类与类之间最普遍的关系就是关联关系. 在 UML 中, 关联是有方向的.  以 Customer 和 Order 为例: 一个用户能发出多个订单, 而一个订单仅仅能属于一个客户. 从 ...