今日后开启进阶模式!

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

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

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

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

       /// <summary>
/// 基础仓储类型接口
/// 采用泛型类接口定义
/// </summary>
/// <typeparam name="T">泛型参数</typeparam>
public interface IRepository<T> : IDisposable where T : class
{
/// <summary>
/// 返回当前表的所有记录
/// </summary>
/// <returns>T</returns>
IQueryable<T> Entries(); /// <summary>
/// 通过过滤条件进行查询
/// </summary>
/// <param name="predicate">过滤条件表达式</param>
/// <returns>T</returns>
IQueryable<T> Filter(Expression<Func<T, bool>> predicate); /// <summary>
/// 通过过滤条件进行查询
/// </summary>
/// <param name="predicate">过滤条件表达式</param>
/// <param name="includes">需贪婪加载的属性名称</param>
/// <returns>IQueryable</returns>
IQueryable<T> Filter(Expression<Func<T, bool>> predicate, params string[] includes); /// <summary>
/// 是否存在满足表达式的记录
/// </summary>
/// <param name="predicate">过滤条件表达式</param>
/// <returns>Boolean</returns>
bool Contains(Expression<Func<T, bool>> predicate); /// <summary>
/// 按照数据库主键查询特定的实例
/// </summary>
/// <param name="keys">主键列表</param>
/// <returns>T</returns>
T Single(params object[] keys); /// <summary>
/// 按照指定表达式查询特定的实例
/// </summary>
/// <param name="predicate">过滤条件表达式</param>
/// <returns>T</returns>
T FirstOrDefault(Expression<Func<T, bool>> predicate); /// <summary>
/// 插入一条记录
/// </summary>
/// <param name="t">新实例</param>
/// <param name="submitImmediately">是否直接提交。默认false。</param>
/// <returns>T</returns>
T Create(T t, bool submitImmediately = false); /// <summary>
/// 删除一行记录
/// </summary>
/// <param name="t">要删除的实例</param>
/// <param name="submitImmediately">是否直接提交。默认false。</param>
void Delete(T t, bool submitImmediately = false); /// <summary>
/// 删除满足表达式的记录
/// </summary>
/// <param name="predicate">过滤条件表达式</param>
/// <param name="submitImmediately">是否直接提交。默认false。</param>
void Delete(Expression<Func<T, bool>> predicate, bool submitImmediately = false); /// <summary>
/// 更新一条记录
/// </summary>
/// <param name="t">要更新的实例</param>
/// <param name="submitImmediately">是否直接提交。默认false。</param>
void Update(T t, bool submitImmediately = false); /// <summary>
/// 获取当前实例的主键
/// </summary>
/// <param name="t">实例</param>
/// <returns>Object</returns>
object GetKeyValue(T t);
}

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

       /// <summary>
/// 仓储类型定义
/// 采用泛型类定义
/// </summary>
/// <typeparam name="T">泛型参数</typeparam>
public class Repository<T> : IRepository<T> where T : class
{
/// <summary>
/// 数据库上下文
/// </summary>
public UsersContext Context { get; private set; } /// <summary>
/// 当前表记录集合
/// </summary>
protected DbSet<T> DbSet
{
get
{
return Context.Set<T>();
}
} /// <summary>
/// 构造器
/// </summary>
public Repository()
{
Context = new UsersContext();
} /// <summary>
/// 构造器
/// </summary>
/// <param name="connectionString">连接字符串(名称)</param>
public Repository(string connectionString)
{
Context = new UsersContext(connectionString);
} /// <summary>
/// 构造器
/// </summary>
/// <param name="context">数据库上下文</param>
public Repository(UsersContext context)
{
Context = context;
} /// <summary>
/// 析构器
/// </summary>
public void Dispose()
{
if (Context != null)
Context.Dispose();
} /// <summary>
/// 返回当前表的所有记录
/// </summary>
/// <returns></returns>
public IQueryable<T> Entries()
{
return DbSet.AsQueryable();
} /// <summary>
/// 通过过滤条件进行查询
/// </summary>
/// <param name="predicate">过滤条件表达式</param>
/// <returns>T</returns>
public IQueryable<T> Filter(Expression<Func<T, bool>> predicate)
{
return DbSet.Where(predicate);
} /// <summary>
/// 通过过滤条件进行查询
/// </summary>
/// <param name="predicate">过滤条件表达式</param>
/// <param name="includes">需贪婪加载的属性名称</param>
/// <returns>IQueryable</returns>
public IQueryable<T> Filter(Expression<Func<T, bool>> predicate, params string[] includes)
{
var query = DbSet.Where(predicate);
if (includes != null)
{
foreach (var item in includes)
{
query = query.Include(item);
}
}
return query;
} /// <summary>
/// 是否存在满足表达式的记录
/// </summary>
/// <param name="predicate">过滤条件表达式</param>
/// <returns>Boolean</returns>
public bool Contains(Expression<Func<T, bool>> predicate)
{
return DbSet.Count(predicate) > ;
} /// <summary>
/// 按照数据库主键查询特定的实例
/// </summary>
/// <param name="keys">主键列表</param>
/// <returns>T</returns>
public T Single(params object[] keys)
{
return DbSet.Find(keys);
} /// <summary>
/// 按照指定表达式查询特定的实例
/// </summary>
/// <param name="predicate">过滤条件表达式</param>
/// <returns>T</returns>
public T FirstOrDefault(Expression<Func<T, bool>> predicate)
{
return DbSet.FirstOrDefault(predicate);
} /// <summary>
/// 插入一条记录
/// </summary>
/// <param name="t">新实例</param>
/// <param name="submitImmediately">是否直接提交。默认false。</param>
/// <returns>T</returns>
public T Create(T t, bool submitImmediately = false)
{
var newEntry = DbSet.Add(t);
if (submitImmediately)
Context.SaveChanges();
return newEntry;
} /// <summary>
/// 删除一行记录
/// </summary>
/// <param name="t">要删除的实例</param>
/// <param name="submitImmediately">是否直接提交。默认false。</param>
public void Delete(T t, bool submitImmediately = false)
{
DbSet.Remove(t);
if (submitImmediately)
Context.SaveChanges();
} /// <summary>
/// 删除满足表达式的记录
/// </summary>
/// <param name="predicate">过滤条件表达式</param>
/// <param name="submitImmediately">是否直接提交。默认false。</param>
public void Delete(Expression<Func<T, bool>> predicate, bool submitImmediately = false)
{
try
{
Context.Configuration.AutoDetectChangesEnabled = false; //关闭数据库上下文的自动更新跟踪功能,可提高批量操作的性能 var objects = Filter(predicate);
foreach (var obj in objects)
DbSet.Remove(obj);
if (submitImmediately)
Context.SaveChanges();
}
finally
{
Context.Configuration.AutoDetectChangesEnabled = true; //完成批量操作后,打开数据库上下文的自动更新跟踪功能
}
} /// <summary>
/// 更新一条记录
/// </summary>
/// <param name="t">要更新的实例</param>
/// <param name="submitImmediately">是否直接提交。默认false。</param>
public void Update(T t, bool submitImmediately = false)
{
var key = GetKeyValue(t); var originalEntity = DbSet.Find(key); Context.Entry(originalEntity).CurrentValues.SetValues(t); if (submitImmediately)
Context.SaveChanges();
} /// <summary>
/// 获取当前实例的主键
/// </summary>
/// <param name="t">实例</param>
/// <returns>Object</returns>
public object GetKeyValue(T t)
{
var key =
typeof(T).GetProperties().FirstOrDefault(
p => p.GetCustomAttributes(typeof(System.ComponentModel.DataAnnotations.KeyAttribute), true).Length != );
return (key != null) ? key.GetValue(t, null) : null;
}
}

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

  接下来就是我们的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如何工作,大家不必太较真。

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

.NET MVC4 实训记录之四(Unit of work + Repository)的更多相关文章

  1. .NET MVC4 实训记录之二(扩展WebSecurity模型下的UserProfile表)

    使用VS2013创建MVC4项目后,自动生成的代码中默认使用WebSecurity模型创建用户管理,生成以下数据库:

  2. .NET MVC4 实训记录之六(利用ModelMetadata实现资源的自主访问)

    上一篇我们已经实现自定义资源文件的访问,该篇我们使用它配合ModelMetadata实现资源文件的自主访问.这样做是为了我们能更简单的用MVC原生的方式使用资源文件.由于我的文章旨在记录MVC项目的实 ...

  3. .NET MVC4 实训记录之一(引入Unity3.0 Ioc框架)

    一直在做维护项目,没有机会接触完整的架构,于是自学.NET MVC.自今日起,将自学的过程.遇到的问题以及解决方案记录下来. 在WebApp项目中右键,使用NuGet引入Unity3.0.

  4. .NET MVC4 实训记录之七(实现资源的自主访问后续)

    我们在上一篇中讨论了如何利用ModelMetadata实现国际化资源文件访问,但也留下了一些问题,即:如何利用ModelMetadata实现相同类型的属性信息的个性化资源显示.本人没有找到合适的方案, ...

  5. .NET MVC4 实训记录之五(访问自定义资源文件)

    .Net平台下工作好几年了,资源文件么,大多数使用的是.resx文件.它是个好东西,很容易上手,工作效率高,性能稳定.使用.resx文件,会在编译期动态生成已文件名命名的静态类,因此它的访问速度当然是 ...

  6. .NET MVC4 实训记录之三(EntityFramework 与枚举)

    EntityFramework对枚举的引入是从版本5开始的(如果没有记错的话).枚举可以很大程度上提高对程序的可读性.那么在EntityFramework的CodeFirst模式下,如何使用枚举呢?答 ...

  7. 实训任务05 MapReduce获取成绩表的最高分记录

    实训任务05  MapReduce获取成绩表的最高分记录 实训1:统计用户纺问次数 任务描述: 统计用户在2016年度每个自然日的总访问次数.原始数据文件中提供了用户名称与访问日期.这个任务就是要获取 ...

  8. UML基础与Rose建模实训教程

    目  录 第1章  初识UML. 1 1.1 初识UML用例图... 1 1.2 初识UML类图... 3 第2章  Rational Rose工具... 6 2.1 安装与配置Rational Ro ...

  9. <实训|第四天>Linux下的vim你真的掌握了吗?附上ftp远程命令上传。

    期待已久的linux运维.oracle"培训班"终于开班了,我从已经开始长期四个半月的linux运维.oracle培训,每天白天我会好好学习,晚上回来我会努力更新教程,包括今天学到 ...

随机推荐

  1. Bootstrap(2)整体架构

    Bootstrap(2)整体架构 大多数Bootstrap的使用者都认为Bootstrap只提供了CSS组件 和JavaScript插件,其实CSS组件和JavaScript插件只是Bootstrap ...

  2. 霸气侧漏HTML5--之--canvas(1) api + 弹球例子

    html5也许最有吸引力的新功能是canvas 漆.基本可以足够强大后,以取代flash页面的效果.下面来介绍canvas要使用: HTML5 Canvas的基本图形都是以路径为基础的.通常使用Con ...

  3. 30分钟让你了解MongoDB基本操作(转)

    今天记录下MongoDB的基本操作,这只是最基本的,所以是应该掌握的. 数据库 数据库是一个物理容器集合.每个数据库都有自己的一套文件系统上的文件.一个单一的MongoDB服务器通常有多个数据库. 集 ...

  4. 首先运行application的name相应的类或做activity中间name相应的类?

    今天找到该程序条目中找到以下两个条件name我写了一个测试程序,一般如以下: 看mainfest.xml <application android:allowBackup="true& ...

  5. hdu 3449 (有依赖的01背包)

    依赖背包 事实上,这是一种树形DP,其特点是每个父节点都需要对它的各个儿子的属性进行一次DP以求得自己的相关属性. fj打算去买一些东西,在那之前,他需要一些盒子去装他打算要买的不同的物品.每一个盒子 ...

  6. C#关于HttpClient的应用(二):融云IM集成

    public class RcHttpClient:BaseHttpClient { private String appKey; private String appSecret; public R ...

  7. Server Tomcat v7.0 Server at localhost was unable to start within 45 seconds解

    产生了一个解决这个问题的方法是在项目部署到tomcat比长45第二,当项目是比较大的,框架复杂的问题经常发生. 解决方法非常easy,找到以下这个路径中 workspace\.metadata\.pl ...

  8. CSharp设计模式读书笔记(14):职责链模式(学习难度:★★★☆☆,使用频率:★★☆☆☆)

    职责链模式(Chain of Responsibility  Pattern):避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象 ...

  9. Windows系统下Redis的安装

    Redis是一个用的比较广泛的Key/Value的内存数据库,新浪微博.Github.StackOverflow 等大型应用中都用其作为缓存,Redis的官网为http://redis.io/. 最近 ...

  10. 让你的Windows不断重启的C语言代码

    原文:让你的Windows不断重启的C语言代码 没有写Linux的原因是因为搞不定Linux下的权限问题,而Windows下基本上使用电脑的用户都是管理员,所以钻个空了,不多说下面是代码#includ ...