一步一步学EF系列 【7、结合IOC ,Repository,UnitOfWork来完成框架的搭建】
前言
距离上一篇已经有段时间了,最近这段时间赶上新项目开发,一直没有时间来写。之前的几篇文章,主要把EF的基础都讲了一遍,这批文章就来个实战篇。
个人在学习过程中参考博客:
- Entity Framework技术系列
- EF-Code First(1):Repository,UnitOfWork,DbContext
Repository
在数据库系统中,对于数据层来说,所有的操作归根结底无非“C(增加)、R(读取)、U(修改)、D(删除)”这四种操作。四种操作当中,与与业务相关度最大的是读取操作,根据各种不同的业务需求提交不同的查询,其最终执行应该放到业务层面中去进行,而增加,修改,删除这三种操作较为通用,可以作为通用数据操作封装到Repository中。在Repository中,唯一的变化点就是各种不同的实体类型,既然是变化点就应该进行封装,这里使用泛型来封装这个变化点。
还要说明一下,每个操作方法都带有一个 isSave 可选参数,是为了单个实体操作的需要,免去了每次都要调用 context.SaveChanged()的麻烦。如果是进行多个实体的单元事务操作,就需要把这个参数设置为 false 。
这里面就是定义了一个实体的增删改查。
/// <summary> /// 定义仓储模型中的数据标准操作 /// </summary> /// <typeparam name="TEntity">动态实体类型</typeparam> /// <typeparam name="TKey">实体主键类型</typeparam> public interface IRepository<TEntity, in TKey> : IDependency where TEntity : EntityBase<TKey> { #region 属性 /// <summary> /// 获取 当前实体的查询数据集 /// </summary> IQueryable<TEntity> Entities { get; } #endregion #region 公共方法 /// <summary> /// 插入实体记录 /// </summary> /// <param name="entity"> 实体对象 </param> /// <param name="isSave"> 是否执行保存 </param> /// <returns> 操作影响的行数 </returns> int Insert(TEntity entity, bool isSave = true); /// <summary> /// 批量插入实体记录集合 /// </summary> /// <param name="entities"> 实体记录集合 </param> /// <param name="isSave"> 是否执行保存 </param> /// <returns> 操作影响的行数 </returns> int Insert(IEnumerable<TEntity> entities, bool isSave = true); /// <summary> /// 删除指定编号的记录 /// </summary> /// <param name="id"> 实体记录编号 </param> /// <param name="isSave"> 是否执行保存 </param> /// <returns> 操作影响的行数 </returns> int Delete(TKey id, bool isSave = true); /// <summary> /// 删除实体记录 /// </summary> /// <param name="entity"> 实体对象 </param> /// <param name="isSave"> 是否执行保存 </param> /// <returns> 操作影响的行数 </returns> int Delete(TEntity entity, bool isSave = true); /// <summary> /// 删除实体记录集合 /// </summary> /// <param name="entities"> 实体记录集合 </param> /// <param name="isSave"> 是否执行保存 </param> /// <returns> 操作影响的行数 </returns> int Delete(IEnumerable<TEntity> entities, bool isSave = true); /// <summary> /// 删除所有符合特定表达式的数据 /// </summary> /// <param name="predicate"> 查询条件谓语表达式 </param> /// <param name="isSave"> 是否执行保存 </param> /// <returns> 操作影响的行数 </returns> int Delete(Expression<Func<TEntity, bool>> predicate, bool isSave = true); /// <summary> /// 更新实体记录 /// </summary> /// <param name="entity"> 实体对象 </param> /// <param name="isSave"> 是否执行保存 </param> /// <returns> 操作影响的行数 </returns> int Update(TEntity entity, bool isSave = true); /// <summary> /// 更新实体记录集合 /// </summary> /// <param name="entity"> 实体对象 </param> /// <param name="isSave"> 是否执行保存 </param> /// <returns> 操作影响的行数 </returns> int Update(IEnumerable<TEntity> entitys, bool isSave = true); /// <summary> /// 查找指定主键的实体记录 /// </summary> /// <param name="key"> 指定主键 </param> /// <returns> 符合编号的记录,不存在返回null </returns> TEntity GetByKey(TKey key); #endregion }
Repository的通用实现如下:
/// <summary> /// EntityFramework仓储操作基类 /// </summary> /// <typeparam name="TEntity">动态实体类型</typeparam> /// <typeparam name="TKey">实体主键类型</typeparam> public abstract class EFRepositoryBase<TEntity, TKey> : IRepository<TEntity, TKey> where TEntity : EntityBase<TKey> { #region 属性 /// <summary> /// 获取 仓储上下文的实例 /// </summary> public IUnitOfWork UnitOfWork { get; set; } /// <summary> /// 获取 EntityFramework的数据仓储上下文 /// </summary> protected UnitOfWorkContextBase EFContext { get { if (UnitOfWork is UnitOfWorkContextBase) { return UnitOfWork as UnitOfWorkContextBase; } throw new DataAccessException(string.Format("数据仓储上下文对象类型不正确,应为UnitOfWorkContextBase,实际为 {0}", UnitOfWork.GetType().Name)); } } /// <summary> /// 获取 当前实体的查询数据集 /// </summary> public virtual IQueryable<TEntity> Entities { get { return EFContext.Set<TEntity, TKey>(); } } #endregion #region 公共方法 /// <summary> /// 插入实体记录 /// </summary> /// <param name="entity"> 实体对象 </param> /// <param name="isSave"> 是否执行保存 </param> /// <returns> 操作影响的行数 </returns> public virtual int Insert(TEntity entity, bool isSave = true) { PublicHelper.CheckArgument(entity, "entity"); EFContext.RegisterNew<TEntity, TKey>(entity); return isSave ? EFContext.Commit() : 0; } /// <summary> /// 批量插入实体记录集合 /// </summary> /// <param name="entities"> 实体记录集合 </param> /// <param name="isSave"> 是否执行保存 </param> /// <returns> 操作影响的行数 </returns> public virtual int Insert(IEnumerable<TEntity> entities, bool isSave = true) { PublicHelper.CheckArgument(entities, "entities"); EFContext.RegisterNew<TEntity, TKey>(entities); return isSave ? EFContext.Commit() : 0; } /// <summary> /// 删除指定编号的记录 /// </summary> /// <param name="id"> 实体记录编号 </param> /// <param name="isSave"> 是否执行保存 </param> /// <returns> 操作影响的行数 </returns> public virtual int Delete(TKey id, bool isSave = true) { PublicHelper.CheckArgument(id, "id"); TEntity entity = EFContext.Set<TEntity, TKey>().Find(id); return entity != null ? Delete(entity, isSave) : 0; } /// <summary> /// 删除实体记录 /// </summary> /// <param name="entity"> 实体对象 </param> /// <param name="isSave"> 是否执行保存 </param> /// <returns> 操作影响的行数 </returns> public virtual int Delete(TEntity entity, bool isSave = true) { PublicHelper.CheckArgument(entity, "entity"); EFContext.RegisterDeleted<TEntity, TKey>(entity); return isSave ? EFContext.Commit() : 0; } /// <summary> /// 删除实体记录集合 /// </summary> /// <param name="entities"> 实体记录集合 </param> /// <param name="isSave"> 是否执行保存 </param> /// <returns> 操作影响的行数 </returns> public virtual int Delete(IEnumerable<TEntity> entities, bool isSave = true) { PublicHelper.CheckArgument(entities, "entities"); EFContext.RegisterDeleted<TEntity, TKey>(entities); return isSave ? EFContext.Commit() : 0; } /// <summary> /// 删除所有符合特定表达式的数据 /// </summary> /// <param name="predicate"> 查询条件谓语表达式 </param> /// <param name="isSave"> 是否执行保存 </param> /// <returns> 操作影响的行数 </returns> public virtual int Delete(Expression<Func<TEntity, bool>> predicate, bool isSave = true) { PublicHelper.CheckArgument(predicate, "predicate"); List<TEntity> entities = EFContext.Set<TEntity, TKey>().Where(predicate).ToList(); return entities.Count > 0 ? Delete(entities, isSave) : 0; } /// <summary> /// 更新实体记录 /// </summary> /// <param name="entity"> 实体对象 </param> /// <param name="isSave"> 是否执行保存 </param> /// <returns> 操作影响的行数 </returns> public virtual int Update(TEntity entity, bool isSave = true) { PublicHelper.CheckArgument(entity, "entity"); EFContext.RegisterModified<TEntity, TKey>(entity); return isSave ? EFContext.Commit() : 0; } /// <summary> /// 批量注册一个更改的对象到仓储上下文中 /// </summary> /// <typeparam name="TEntity"> 要注册的类型 </typeparam> /// <typeparam name="TKey">实体主键类型</typeparam> /// <param name="entity"> 要注册的对象 </param> public virtual int Update(IEnumerable<TEntity> entities, bool isSave = true) { PublicHelper.CheckArgument(entities, "entities"); EFContext.RegisterModified<TEntity, TKey>(entities); return isSave ? EFContext.Commit() : 0; } /// <summary> /// 查找指定主键的实体记录 /// </summary> /// <param name="key"> 指定主键 </param> /// <returns> 符合编号的记录,不存在返回null </returns> public virtual TEntity GetByKey(TKey key) { PublicHelper.CheckArgument(key, "key"); return EFContext.Set<TEntity, TKey>().Find(key); } #endregion }
实现类中所有操作最终都是通过单元操作来提交的,关于单元操作,接下来。这里用到了IOC,不懂的可以看之前的文章
UnitOfWork
引入单元操作,主要是为了给各个实体维护一个共同的DbContext上下文对象,保证所有的操作都是在共同的上下文中进行的。EF的操作提交 context.SaveChanged() 默认就是事务性的,只要保证了当前的所有实体的操作都是在一个共同的上下文中进行的,就实现了事务操作了。
在业务层中,各个实体的增删改操作都是通过各个实体的Repository进行的,只需要提供一个提交保存的功能作为最后调用,即可保证当前的提交是事务性的。因此定义给业务层引用的单元操作接口如下:
/// <summary> /// 业务单元操作接口 /// </summary> public interface IUnitOfWork : IDependency { #region 属性 /// <summary> /// 获取 当前单元操作是否已被提交 /// </summary> bool IsCommitted { get; } #endregion #region 方法 /// <summary> /// 提交当前单元操作的结果 /// </summary> /// <param name="validateOnSaveEnabled">保存时是否自动验证跟踪实体</param> /// <returns></returns> int Commit(bool validateOnSaveEnabled = true); /// <summary> /// 把当前单元操作回滚成未提交状态 /// </summary> void Rollback(); #endregion }
在数据组件内部,数据操作最终都提交到一个与IUnitOfWork接口的实现类中进行操作,以保证各个实体的Repository与IUnitOfWork使用的是同一个DbContext上下文。定义数据单元操作接口如下:
/// <summary> /// 数据单元操作接口 /// </summary> public interface IUnitOfWorkContext : IUnitOfWork, IDisposable { /// <summary> /// 注册一个新的对象到仓储上下文中 /// </summary> /// <typeparam name="TEntity"> 要注册的类型 </typeparam> /// <typeparam name="TKey">实体主键类型</typeparam> /// <param name="entity"> 要注册的对象 </param> void RegisterNew<TEntity, TKey>(TEntity entity) where TEntity : EntityBase<TKey>; /// <summary> /// 批量注册多个新的对象到仓储上下文中 /// </summary> /// <typeparam name="TEntity"> 要注册的类型 </typeparam> /// <typeparam name="TKey">实体主键类型</typeparam> /// <param name="entities"> 要注册的对象集合 </param> void RegisterNew<TEntity, TKey>(IEnumerable<TEntity> entities) where TEntity : EntityBase<TKey>; /// <summary> /// 注册一个更改的对象到仓储上下文中 /// </summary> /// <typeparam name="TEntity"> 要注册的类型 </typeparam> /// <typeparam name="TKey">实体主键类型</typeparam> /// <param name="entity"> 要注册的对象 </param> void RegisterModified<TEntity, TKey>(TEntity entity) where TEntity : EntityBase<TKey>; /// <summary> /// 注册一个删除的对象到仓储上下文中 /// </summary> /// <typeparam name="TEntity"> 要注册的类型 </typeparam> /// <typeparam name="TKey">实体主键类型</typeparam> /// <param name="entity"> 要注册的对象 </param> void RegisterDeleted<TEntity, TKey>(TEntity entity) where TEntity : EntityBase<TKey>; /// <summary> /// 批量注册多个删除的对象到仓储上下文中 /// </summary> /// <typeparam name="TEntity"> 要注册的类型 </typeparam> /// <typeparam name="TKey">实体主键类型</typeparam> /// <param name="entities"> 要注册的对象集合 </param> void RegisterDeleted<TEntity, TKey>(IEnumerable<TEntity> entities) where TEntity : EntityBase<TKey>; }
在单元操作的实现基类中,定义一个只读的DbContext抽象属性,实际的DbContext上下文需要在实现类中进行重写赋值。
/// <summary> /// 单元操作实现基类 /// </summary> public abstract class UnitOfWorkContextBase : IUnitOfWorkContext { /// <summary> /// 获取 当前使用的数据访问上下文对象 /// </summary> protected abstract DbContext Context { get; } /// <summary> /// 获取 当前单元操作是否已被提交 /// </summary> public bool IsCommitted { get; private set; } public DbContext DbContext { get { return Context; } } /// <summary> /// 提交当前单元操作的结果 /// </summary> /// <param name="validateOnSaveEnabled">保存时是否自动验证跟踪实体</param> /// <returns></returns> public int Commit(bool validateOnSaveEnabled = true) { if (IsCommitted) { return 0; } try { int result = Context.SaveChanges(validateOnSaveEnabled); IsCommitted = true; return result; } catch (DbUpdateException e) { throw; } } /// <summary> /// 把当前单元操作回滚成未提交状态 /// </summary> public void Rollback() { IsCommitted = false; } public void Dispose() { //if (!IsCommitted) //{ // Commit(); //} Context.Dispose(); } /// <summary> /// 为指定的类型返回 System.Data.Entity.DbSet,这将允许对上下文中的给定实体执行 CRUD 操作。 /// </summary> /// <typeparam name="TEntity"> 应为其返回一个集的实体类型。 </typeparam> /// <typeparam name="TKey">实体主键类型</typeparam> /// <returns> 给定实体类型的 System.Data.Entity.DbSet 实例。 </returns> public DbSet<TEntity> Set<TEntity, TKey>() where TEntity : EntityBase<TKey> { return Context.Set<TEntity>(); } /// <summary> /// 注册一个新的对象到仓储上下文中 /// </summary> /// <typeparam name="TEntity"> 要注册的类型 </typeparam> /// <typeparam name="TKey">实体主键类型</typeparam> /// <param name="entity"> 要注册的对象 </param> public void RegisterNew<TEntity, TKey>(TEntity entity) where TEntity : EntityBase<TKey> { EntityState state = Context.Entry(entity).State; if (state == EntityState.Detached) { Context.Entry(entity).State = EntityState.Added; } IsCommitted = false; } /// <summary> /// 批量注册多个新的对象到仓储上下文中 /// </summary> /// <typeparam name="TEntity"> 要注册的类型 </typeparam> /// <typeparam name="TKey">实体主键类型</typeparam> /// <param name="entities"> 要注册的对象集合 </param> public void RegisterNew<TEntity, TKey>(IEnumerable<TEntity> entities) where TEntity : EntityBase<TKey> { try { //禁用自动发现功能 Context.Configuration.AutoDetectChangesEnabled = false; foreach (TEntity entity in entities) { RegisterNew<TEntity, TKey>(entity); } } finally { Context.Configuration.AutoDetectChangesEnabled = true; } } /// <summary> /// 注册一个更改的对象到仓储上下文中 /// </summary> /// <typeparam name="TEntity"> 要注册的类型 </typeparam> /// <typeparam name="TKey">实体主键类型</typeparam> /// <param name="entity"> 要注册的对象 </param> public void RegisterModified<TEntity, TKey>(TEntity entity) where TEntity : EntityBase<TKey> { Context.Update<TEntity, TKey>(entity); IsCommitted = false; } /// <summary> /// 批量注册一个更改的对象到仓储上下文中 /// </summary> /// <typeparam name="TEntity"> 要注册的类型 </typeparam> /// <typeparam name="TKey">实体主键类型</typeparam> /// <param name="entity"> 要注册的对象 </param> public void RegisterModified<TEntity, TKey>(IEnumerable<TEntity> entities) where TEntity : EntityBase<TKey> { Context.Update<TEntity, TKey>(entities.ToArray()); IsCommitted = false; } /// <summary> /// 注册一个删除的对象到仓储上下文中 /// </summary> /// <typeparam name="TEntity"> 要注册的类型 </typeparam> /// <typeparam name="TKey">实体主键类型</typeparam> /// <param name="entity"> 要注册的对象 </param> public void RegisterDeleted<TEntity, TKey>(TEntity entity) where TEntity : EntityBase<TKey> { Context.Entry(entity).State = EntityState.Deleted; IsCommitted = false; } /// <summary> /// 批量注册多个删除的对象到仓储上下文中 /// </summary> /// <typeparam name="TEntity"> 要注册的类型 </typeparam> /// <typeparam name="TKey">实体主键类型</typeparam> /// <param name="entities"> 要注册的对象集合 </param> public void RegisterDeleted<TEntity, TKey>(IEnumerable<TEntity> entities) where TEntity : EntityBase<TKey> { try { Context.Configuration.AutoDetectChangesEnabled = false; foreach (TEntity entity in entities) { RegisterDeleted<TEntity, TKey>(entity); } } finally { Context.Configuration.AutoDetectChangesEnabled = true; } } }
业务整合
首先看接口的定义 ,他需要继承仓储接口
public interface IUserService : IRepository<Models.User, int> { }
实现类继承仓储的基类
/// <summary> /// 仓储操作层实现——登录记录信息 /// </summary> public partial class UserRepository : EFRepositoryBase<Models.User, int>, IUserService { } }
主要看一下使用,这里使用的是属性注入,然后就可以调用了
public class UserService : IDependency { public IUserService userService { get; set; } /// <summary> /// 获取 当前实体的查询数据集 /// </summary> public virtual IQueryable<Models.User> User { get { return userService.Entities; } } public Models.User GetByKey(int id) { try { } catch (Exception) { throw new BusinessException(); } return userService.GetByKey(id); } /// <summary> /// 批量插入实体记录集合 /// </summary> /// <param name="entities"> 实体记录集合 </param> /// <param name="isSave"> 是否执行保存 </param> /// <returns> 操作影响的行数 </returns> public int Insert(IEnumerable<Models.User> entities) { return userService.Insert(entities); } /// <summary> /// 注册一个更改的对象到仓储上下文中 /// </summary> /// <typeparam name="TEntity"> 要注册的类型 </typeparam> /// <typeparam name="TKey">实体主键类型</typeparam> /// <param name="entity"> 要注册的对象 </param> public int RegisterModified(Models.User entity) { return userService.Update(entity); } /// <summary> /// 注册一个更改的对象到仓储上下文中 /// </summary> /// <typeparam name="TEntity"> 要注册的类型 </typeparam> /// <typeparam name="TKey">实体主键类型</typeparam> /// <param name="entity"> 要注册的对象 </param> public int RegisterModified(IEnumerable<Models.User> entity) { return userService.Update(entity); } }
前端页面 OK完成,。
public UserService userserver { get; set; } public ActionResult Index() { //Models.User user = userserver.GetByKey(1); //ViewBag.Name = user.UserName; //1、批量新增 //userserver.Insert(new List<Models.User> { // new Models.User() { UserName="张三",Password="123456" }, // new Models.User() { UserName="李四",Password="123456" } //}); // //2、普通更新 //Models.User user = userserver.User.Single(m => m.UserID == 1); //user.UserName = "张三1"; //// userserver.RegisterModified(user); //Models.User user1 = userserver.User.Single(m => m.UserID == 2); //user1.Password = "456789"; ////批量更新 //userserver.RegisterModified(new List<Models.User> //{ // user, user1}); return View(); }
结束语
本文最难理解的就是这个思路,如果能看懂的肯定会发现不足的地方,后面也准备了一个升级篇。不懂的可以加入下面的QQ群进行交流,源代码也已经上传到群共享文件了。欢迎下载。
作者:STONE刘先生 出处:http://www.cnblogs.com/liupeng/
本文版权归作者和博客园共有,欢迎转载。未经作者同意下,必须在文章页面明显标出原文链接及作者,否则保留追究法律责任的权利。
如果您认为这篇文章还不错或者有所收获,可以点击右下角的【推荐】按钮,因为你的支持是我继续写作,分享的最大动力!