一步一步學EF系列 【七、結合IOC ,Repository,UnitOfWork來完成框架的搭建】

前言 html

     距離上一篇已經有段時間了,最近這段時間遇上新項目開發,一直沒有時間來寫。以前的幾篇文章,主要把EF的基礎都講了一遍,這批文章就來個實戰篇。前端

    我的在學習過程當中參考博客:數據庫

  1. Entity Framework技術系列
  2. EF-Code First(1):Repository,UnitOfWork,DbContext

Repository

     在數據庫系統中,對於數據層來講,全部的操做歸根結底無非「C(增長)、R(讀取)、U(修改)、D(刪除)」這四種操做。四種操做當中,與與業務相關度最大的是讀取操做,根據各類不一樣的業務需求提交不一樣的查詢,其最終執行應該放到業務層面中去進行,而增長,修改,刪除這三種操做較爲通用,能夠做爲通用數據操做封裝到Repository中。在Repository中,惟一的變化點就是各類不一樣的實體類型,既然是變化點就應該進行封裝,這裏使用泛型來封裝這個變化點。mvc

還要說明一下,每一個操做方法都帶有一個 isSave 可選參數,是爲了單個實體操做的須要,免去了每次都要調用 context.SaveChanged()的麻煩。若是是進行多個實體的單元事務操做,就須要把這個參數設置爲 false 。學習

  這裏面就是定義了一個實體的增刪改查。spa

    /// <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,不懂的能夠看以前的文章3d

UnitOfWork

 引入單元操做,主要是爲了給各個實體維護一個共同的DbContext上下文對象,保證全部的操做都是在共同的上下文中進行的。EF的操做提交 context.SaveChanged() 默認就是事務性的,只要保證了當前的全部實體的操做都是在一個共同的上下文中進行的,就實現了事務操做了。orm

  在業務層中,各個實體的增刪改操做都是經過各個實體的Repository進行的,只須要提供一個提交保存的功能做爲最後調用,便可保證當前的提交是事務性的。所以定義給業務層引用的單元操做接口以下:server

 

  /// <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;
            }
        }

    }

業務整合              

 

image

首先看接口的定義 ,他須要繼承倉儲接口htm

   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;

            //一、批量新增
            //userserver.Insert(new List<Models.User> {

            //    new Models.User() { UserName="張三",Password="123456" },
            //    new Models.User() { UserName="李四",Password="123456" }

            //});
            //
            //二、普通更新
            //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羣進行交流,源代碼也已經上傳到羣共享文件了。歡迎下載。

你們也能夠加入QQ羣進行交流(435498053)。輕鬆作生意外貿軟件

做者:STONE劉先生 出處:http://www.cnblogs.com/liupeng/

本文版權歸做者和博客園共有,歡迎轉載。未經做者贊成下,必須在文章頁面明顯標出原文連接及做者,不然保留追究法律責任的權利。若是您認爲這篇文章還不錯或者有所收穫,能夠點擊右下角的【推薦】按鈕,由於你的支持是我繼續寫做,分享的最大動力!

相關文章
相關標籤/搜索