應用程序框架實戰二十二 : DDD分層架構之倉儲(層超類型基礎篇)


  前一篇介紹了倉儲的基本概念,並談了我對倉儲的一些認識,本文將實現倉儲的基本功能。

  倉儲代表聚合在內存中的集合,所以倉儲的接口需要模擬得像一個集合。倉儲中有很多操作都是可以通用的,可以把這部分操作抽取到基類中。

  在Util.Domains項目中創建一個文件夾Repositories,這個文件夾用來放倉儲相關的接口。在Repositories下創建一個倉儲接口IRepository

  把倉儲基接口放到Util.Domains,是因為倉儲接口是在領域層定義的,這與傳統三層架構的數據訪問層接口的位置不同。

  倉儲是基礎設施層的組成部分,位於領域層的下方,按理來說,領域層應該依賴倉儲,但這會導致領域層與具體數據訪問組件耦合,降低了領域層的復用能力。為了讓領域層更加純凈,可以應用依賴倒置原則(DIP。依賴倒置原則提到,高層模塊不應該依賴低層模塊,這里的高層模塊就是領域層,低層模塊是基礎設施層的倉儲。

  依賴倒置原則反轉了兩者的依賴關系,即倉儲反過來依賴於領域層。為了讓領域層可以訪問到倉儲提供的服務,需要抽取倉儲接口,你可以把這些接口放到獨立的程序集中,但這會增加不必要的開銷。一種更好的方法是直接把低層接口放入使用它們的客戶程序集中,這樣可以簡化設計,僅在必要時才分離出獨立的接口程序集。依賴倒置原則不僅適用於倉儲,對於任何可能導致高耦合的基礎設施服務都適用,比如第三方外部接口,發郵件,發短信等。

  在Util.Datas.Ef項目中創建一個倉儲實現類Repository

  倉儲接口IRepository的代碼如下。

using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using Util.Datas; namespace Util.Domains.Repositories { /// <summary>
    /// 倉儲 /// </summary>
    /// <typeparam name="TEntity">實體類型</typeparam>
    /// <typeparam name="TKey">實體標識類型</typeparam>
    public interface IRepository<TEntity, in TKey> where TEntity : class, IAggregateRoot<TKey> { /// <summary>
        /// 添加實體 /// </summary>
        /// <param name="entity">實體</param>
        void Add( TEntity entity ); /// <summary>
        /// 添加實體 /// </summary>
        /// <param name="entities">實體</param>
        void Add( IEnumerable<TEntity> entities ); /// <summary>
        /// 修改實體 /// </summary>
        /// <param name="entity">實體</param>
        void Update( TEntity entity ); /// <summary>
        /// 移除實體 /// </summary>
        /// <param name="id">實體標識</param>
        void Remove( TKey id ); /// <summary>
        /// 移除實體 /// </summary>
        /// <param name="entity">實體</param>
        void Remove( TEntity entity ); /// <summary>
        /// 查找實體集合 /// </summary>
        List<TEntity> FindAll(); /// <summary>
        /// 查找實體集合 /// </summary>
        IQueryable<TEntity> Find(); /// <summary>
        /// 查找實體 /// </summary>
        /// <param name="id">實體標識</param>
        TEntity Find( params object[] id ); /// <summary>
        /// 查找實體列表 /// </summary>
        /// <param name="ids">實體標識列表</param>
        List<TEntity> Find( IEnumerable<TKey> ids ); /// <summary>
        /// 判斷實體是否存在 /// </summary>
        /// <param name="predicate">條件</param>
        bool Exists( Expression<Func<TEntity, bool>> predicate ); /// <summary>
        /// 索引器查找,獲取指定標識的實體 /// </summary>
        /// <param name="id">實體標識</param>
        TEntity this[TKey id] { get; } /// <summary>
        /// 保存 /// </summary>
        void Save(); /// <summary>
        /// 獲取工作單元 /// </summary>
 IUnitOfWork GetUnitOfWork(); } }
using System; namespace Util.Domains.Repositories { /// <summary>
    /// 倉儲 /// </summary>
    /// <typeparam name="TEntity">實體類型</typeparam>
    public interface IRepository<TEntity> : IRepository<TEntity, Guid> where TEntity : class, IAggregateRoot<Guid> { } }

  倉儲實現類Repository的代碼如下。

using System; using System.Collections.Generic; using System.Data.Entity; using System.Linq; using System.Linq.Expressions; using Util.Domains; using Util.Domains.Repositories; namespace Util.Datas.Ef { /// <summary>
    /// 倉儲 /// </summary>
    /// <typeparam name="TEntity">實體類型</typeparam>
    /// <typeparam name="TKey">實體標識類型</typeparam>
    public abstract class Repository<TEntity, TKey> : IRepository<TEntity, TKey> where TEntity : class, IAggregateRoot<TKey> { /// <summary>
        /// 初始化倉儲 /// </summary>
        /// <param name="unitOfWork">工作單元</param>
        protected Repository( IUnitOfWork unitOfWork ) { UnitOfWork = (EfUnitOfWork)unitOfWork; } /// <summary>
        /// Ef工作單元 /// </summary>
        protected EfUnitOfWork UnitOfWork { get; private set; } /// <summary>
        /// 添加實體 /// </summary>
        /// <param name="entity">實體</param>
        public void Add( TEntity entity ) { UnitOfWork.Set<TEntity>().Add( entity ); UnitOfWork.CommitByStart(); } /// <summary>
        /// 添加實體 /// </summary>
        /// <param name="entities">實體</param>
        public void Add( IEnumerable<TEntity> entities ) { if ( entities == null ) return; UnitOfWork.Set<TEntity>().AddRange( entities ); UnitOfWork.CommitByStart(); } /// <summary>
        /// 修改實體 /// </summary>
        /// <param name="entity">實體</param>
        public virtual void Update( TEntity entity ) { UnitOfWork.Entry( entity ).State = EntityState.Modified; UnitOfWork.CommitByStart(); } /// <summary>
        /// 移除實體 /// </summary>
        /// <param name="id">實體標識</param>
        public void Remove( TKey id ) { var entity = Find( id ); if ( entity == null ) return; Remove( entity ); } /// <summary>
        /// 移除實體 /// </summary>
        /// <param name="entity">實體</param>
        public void Remove( TEntity entity ) { UnitOfWork.Set<TEntity>().Remove( entity ); UnitOfWork.CommitByStart(); } /// <summary>
        /// 查找實體集合 /// </summary>
        public List<TEntity> FindAll() { return Find().ToList(); } /// <summary>
        /// 查找實體 /// </summary>
        public IQueryable<TEntity> Find() { return UnitOfWork.Set<TEntity>(); } /// <summary>
        /// 查找實體 /// </summary>
        /// <param name="id">實體標識</param>
        public TEntity Find( params object[] id ) { return UnitOfWork.Set<TEntity>().Find( id ); } /// <summary>
        /// 查找實體列表 /// </summary>
        /// <param name="ids">實體標識列表</param>
        public List<TEntity> Find( IEnumerable<TKey> ids ) { if ( ids == null ) return null; return Find().Where( t => ids.Contains( t.Id ) ).ToList(); } /// <summary>
        /// 索引器查找,獲取指定標識的實體 /// </summary>
        /// <param name="id">實體標識</param>
        public TEntity this[TKey id] { get { return Find( id ); } } /// <summary>
        /// 判斷實體是否存在 /// </summary>
        /// <param name="predicate">條件</param>
        public bool Exists( Expression<Func<TEntity, bool>> predicate ) { return Find().Any( predicate ); } /// <summary>
        /// 保存 /// </summary>
        public void Save() { UnitOfWork.Commit(); } /// <summary>
        /// 獲取工作單元 /// </summary>
        public IUnitOfWork GetUnitOfWork() { return UnitOfWork; } } }
using System; using Util.Domains; namespace Util.Datas.Ef { /// <summary>
    /// 倉儲 /// </summary>
    /// <typeparam name="TEntity">實體類型</typeparam>
    public abstract class Repository<TEntity> : Repository<TEntity, Guid> where TEntity : class, IAggregateRoot<Guid> { /// <summary>
        /// 初始化倉儲 /// </summary>
        /// <param name="unitOfWork">工作單元</param>
        protected Repository( IUnitOfWork unitOfWork ) : base( unitOfWork ) { } } }

  倉儲是對聚合的操作,所以泛型接口聲明IRepository<TEntity, in TKey> where TEntity : class, IAggregateRoot<TKey>對TEntity類型限定為聚合。

  在Repository實現類中,通過注入DbContext工作單元來完成所有的工作。IUnitOfWorkEfUnitOfWork是在應用程序框架實戰十九:工作單元層超類型中定義的,EfUnitOfWork從DbContext派生,它有一個核心方法CommitByStart,用來告訴倉儲,如果開啟了工作單元就等待調用端通過Commit提交,否則立即提交。每個數據更新方法Add、Update、Remove都會調用CommitByStart方法。

  對於使用Entity Framework 進行Update修改操作有多種實現方式。在倉儲基類中實現的Update方法比較通用,但我手工編寫代碼時一般會直接把聚合取出來,修改聚合屬性,再提交工作單元。

  本文介紹了倉儲通用操作的基本實現,后續文章我將介紹如何通過表達式樹對查詢擴展和封裝。

 

  .Net應用程序框架交流QQ群: 386092459,歡迎有興趣的朋友加入討論。

  謝謝大家的持續關注,我的博客地址:http://www.cnblogs.com/xiadao521/

  下載地址:http://files.cnblogs.com/xiadao521/Util.2014.12.17.1.rar

 


免責聲明!

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



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