C#進階系列——DDD領域驅動設計初探(三):倉儲Repository(下)


前言:上篇介紹了下倉儲的代碼架構示例以及簡單分析了倉儲了使用優勢。本章還是繼續來完善下倉儲的設計。上章說了,倉儲的最主要作用的分離領域層和具體的技術架構,使得領域層更加專注領域邏輯。那么涉及到具體的實現的時候我們應該怎么做呢,本章就來說說倉儲里面具體細節方便的知識。

DDD領域驅動設計初探系列文章:

一、對倉儲接口以及實現基類的完善

1、倉儲實現基類的所有方法加上virtual關鍵字,方便具體的倉儲在特定需求的時候override基類的方法。

  //倉儲的泛型實現類
    public class EFBaseRepository<TEntity> : IRepository<TEntity> where TEntity : AggregateRoot
    {
        [Import(typeof(IEFUnitOfWork))]
        public IEFUnitOfWork UnitOfWork { get; set; }

        public EFBaseRepository()
        {
            Regisgter.regisgter().ComposeParts(this);
        }

        public virtual IQueryable<TEntity> Entities
        {
            get { return UnitOfWork.context.Set<TEntity>(); }
        }

        public virtual TEntity GetByKey(object key)
        {
            return UnitOfWork.context.Set<TEntity>().Find(key);
        }

        public virtual IQueryable<TEntity> Find(Expression<Func<TEntity, bool>> express)
        {
            Func<TEntity, bool> lamada = express.Compile();
            return UnitOfWork.context.Set<TEntity>().Where(lamada).AsQueryable<TEntity>();
        }

        public virtual int Insert(TEntity entity)
        {
            UnitOfWork.RegisterNew(entity);
            return UnitOfWork.Commit();
        }

        public virtual int Insert(IEnumerable<TEntity> entities)
        {
            foreach (var obj in entities)
            {
                UnitOfWork.RegisterNew(obj);
            }
            return UnitOfWork.Commit();
        }

        public virtual int Delete(object id)
        {
            var obj = UnitOfWork.context.Set<TEntity>().Find(id);
            if (obj == null)
            {
                return 0;
            }
            UnitOfWork.RegisterDeleted(obj);
            return UnitOfWork.Commit();
        }

        public virtual int Delete(TEntity entity)
        {
            UnitOfWork.RegisterDeleted(entity);
            return UnitOfWork.Commit();
        }

        public virtual int Delete(IEnumerable<TEntity> entities)
        {
            foreach (var entity in entities)
            {
                UnitOfWork.RegisterDeleted(entity);
            }
            return UnitOfWork.Commit();
        }

        public virtual int Delete(Expression<Func<TEntity, bool>> express)
        {
            Func<TEntity, bool> lamada = express.Compile();
            var lstEntity = UnitOfWork.context.Set<TEntity>().Where(lamada);
            foreach (var entity in lstEntity)
            {
                UnitOfWork.RegisterDeleted(entity);
            }
            return UnitOfWork.Commit();
        }

        public virtual int Update(TEntity entity)
        {
            UnitOfWork.RegisterModified(entity);
            return UnitOfWork.Commit();
        }
    }

 

2、查詢和刪除增加了傳參lamada表達式的方法

倉儲接口:

   public interface IRepository<TEntity> where TEntity : AggregateRoot
    {
        //...........

        #region 公共方法

        /// <summary>
        /// 根據lamada表達式查詢集合
        /// </summary>
        /// <param name="selector">lamada表達式</param>
        /// <returns></returns>
        IQueryable<TEntity> Find(Expression<Func<TEntity, bool>> express);

            /// <summary>
        ///     根據lamada表達式刪除對象
        /// </summary>
        /// <param name="selector"> lamada表達式 </param>
        /// <returns> 操作影響的行數 </returns>
        int Delete(Expression<Func<TEntity, bool>> express);

         //..........
  }

倉儲的實現

   //倉儲的泛型實現類
    public class EFBaseRepository<TEntity> : IRepository<TEntity> where TEntity : AggregateRoot
    {
        //.............

        public virtual IQueryable<TEntity> Find(Expression<Func<TEntity, bool>> express)
        {
            Func<TEntity, bool> lamada = express.Compile();
            return UnitOfWork.context.Set<TEntity>().Where(lamada).AsQueryable<TEntity>();
        }

        public virtual int Delete(Expression<Func<TEntity, bool>> express)
        {
            Func<TEntity, bool> lamada = express.Compile();
            var lstEntity = UnitOfWork.context.Set<TEntity>().Where(lamada);
            foreach (var entity in lstEntity)
            {
                UnitOfWork.RegisterDeleted(entity);
            }
            return UnitOfWork.Commit();
        }

    //.............
    }

增加這兩個方法之后,對於單表的一般查詢都可以直接通過lamada表示式的方法傳入即可,並且返回值為IQueryable類型。

 

3、對於涉及到多張表需要連表的查詢機制,我們還是通過神奇的Linq來解決。例如我們有一個通過角色取角色對應的菜單的接口需求。

在菜單的倉儲接口里面:

   /// <summary>
    /// 菜單這個聚合根的倉儲接口
    /// </summary>
    public interface IMenuRepository:IRepository<TB_MENU>
    {
        IQueryable<TB_MENU> GetMenusByRole(TB_ROLE oRole);
    }

對應倉儲實現:

  [Export(typeof(IMenuRepository))]
    public class MenuRepository:EFBaseRepository<TB_MENU>,IMenuRepository
    {
        public IQueryable<TB_MENU> GetMenusByRole(TB_ROLE oRole)
        {
            var queryrole = UnitOfWork.context.Set<TB_ROLE>().AsQueryable();
            var querymenu = UnitOfWork.context.Set<TB_MENU>().AsQueryable();
            var querymenurole = UnitOfWork.context.Set<TB_MENUROLE>().AsQueryable();
            var lstres = from menu in querymenu
                         from menurole in querymenurole
                         from role in queryrole
                         where menu.MENU_ID == menurole.MENU_ID &&
                               menurole.ROLE_ID == role.ROLE_ID &&
                               role.ROLE_ID == oRole.ROLE_ID
                         select menu;
            return lstres;
        }
    }

 這里也是返回的IQueryable接口的集合,千萬不要小看IQueryable接口,它是一種表達式樹,可以延遲查詢。也就是說,在我們執行GetMenusByRole()之后,得到的是一個帶有查詢sql語句的表達式樹結構,並沒有去數據庫執行查詢,只有在我們ToList()的時候才會去查詢數據庫。我們來寫個Demo測試下。

   class Program
    {
        [Import]
        public IUserRepository userRepository { get; set; }

        [Import]
        public IMenuRepository menuRepository { get; set; }

        static void Main(string[] args)
        {
            //注冊MEF
            var oProgram = new Program();
            Regisgter.regisgter().ComposeParts(oProgram);
var lstFindUsers = oProgram.userRepository.Find(x => x.USER_NAME !=null);
            var lstRes = lstFindUsers.ToList();
            var lstMenu = oProgram.menuRepository.GetMenusByRole(new TB_ROLE() { ROLE_ID = "aaaa" });
            var lstMenuRes = lstMenu.ToList();
        }
    }

來看執行過程:

 當程序執行var lstMenu = oProgram.menuRepository.GetMenusByRole(new TB_ROLE() { ROLE_ID = "aaaa" })這一步的時候基本是不耗時的,因為這一步僅僅是在構造表達式樹,只有在.ToList()的時候才會有查詢等待。更多詳細可以看看此文 Repository 返回 IQueryable?還是 IEnumerable?

在dax.net的系列文章中,提到了規約模式的概念,用於解決條件查詢的問題。博主感覺這個東西設計確實牛叉,但實用性不太強,一般中小型的項目也用不上。有興趣可以看看規約(Specification)模式


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM