(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. SQLServer 存储过程嵌套事务处理

    原文:SQLServer 存储过程嵌套事务处理 某个存储过程可能被单独调用,也可能由其他存储过程嵌套调用,则可能会发生嵌套事务的情形. 下面是一种解决存储过程嵌套调用的通用代码,在不能确定存储过程是否 ...

  2. 数组、链表、Hash(转)

    在程序中,存放指定的数据最常用的数据结构有两种:数组和链表. 数组和链表的区别: 1.数组是将元素在内存中连续存放. 链表中的元素在内存中不是顺序存储的,而是通过存在元素中的指针联系到一起. 2.数组 ...

  3. JNLP(Java Web Start )(转)

    JNLP(Java Network Launching Protocol )是java提供的一种可以通过浏览器直接执行java应用程序的途径,它使你可以直接通过一个网页上的url连接打开一个java应 ...

  4. Oracle中的delete和truncate的关系

    转自:http://chenxy.blog.51cto.com/729966/168459 1.相同点       a.都可以删除数据,其中truncate用于全表清空数据,而delete可以选择的删 ...

  5. 使用 CodeIgniter 框架快速开发 PHP 应用(六)

    原文:使用 CodeIgniter 框架快速开发 PHP 应用(六) 简化使用 Session 和安全理论说得够多了! 现在让我们开始写我们自己的应用. 在这一章里,我们将会大致描述一下我们要建立的一 ...

  6. Android相框 与 源代码结构

    一. Android 相框 Android框架层级 : Android 自下 而 上 分为 4层; -- Linux内核层; -- 各种库 和 Android执行环境层; -- 应用框架层; -- 应 ...

  7. SEO要领:8文章主持技巧(两)

    续篇:搜索引擎优化要领:8条辅助技巧(一) 四.检查你的robots.txt文件 与谷歌的蜘蛛通信的经常用法是使用robots.txt文件. 这是一个文本文件.同意你告诉搜索引擎,你的站点的网页上抓取 ...

  8. 变焦控制ZoomControls

    在安卓的webview中有这个点击放大缩小button,当时就在想如何实现那种效果,弄一个两个图标的ImageButton.但感觉又有些麻烦,昨天看疯狂安卓书.无意中发现另一个 ZoomButtons ...

  9. SQL字符串转换为数组

    /*一.按指定符号分割字符串,返回分割后的元素个数,方法很简单,就是看字符串中存在多少个分隔符号,然后再加一,就是要求的结果. -----rtrim(@str)去掉 @str右边的字符 ltrim(@ ...

  10. 取一种类型里面的产品销售前3甲的数据Sql

    需求:取出每种分类里面的销售前3甲的产品信息 表设计如下图: 数据如下: 两种方法可以实现: 1. SELECT * FROM (SELECT ROW_NUMBER() OVER(PARTITION ...