前言
距離上一篇已經有段時間了,最近這段時間趕上新項目開發,一直沒有時間來寫。之前的幾篇文章,主要把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/
本文版權歸作者和博客園共有,歡迎轉載。未經作者同意下,必須在文章頁面明顯標出原文鏈接及作者,否則保留追究法律責任的權利。
如果您認為這篇文章還不錯或者有所收獲,可以點擊右下角的【推薦】按鈕,因為你的支持是我繼續寫作,分享的最大動力!