前一篇介紹了倉儲的基本概念,並談了我對倉儲的一些認識,本文將實現倉儲的基本功能。html
倉儲表明聚合在內存中的集合,因此倉儲的接口須要模擬得像一個集合。倉儲中有不少操做都是能夠通用的,能夠把這部分操做抽取到基類中。架構
在Util.Domains項目中建立一個文件夾Repositories,這個文件夾用來放倉儲相關的接口。在Repositories下建立一個倉儲接口IRepository。框架
把倉儲基接口放到Util.Domains,是由於倉儲接口是在領域層定義的,這與傳統三層架構的數據訪問層接口的位置不一樣。ui
倉儲是基礎設施層的組成部分,位於領域層的下方,按理來講,領域層應該依賴倉儲,但這會致使領域層與具體數據訪問組件耦合,下降了領域層的複用能力。爲了讓領域層更加純淨,能夠應用依賴倒置原則(DIP)。依賴倒置原則提到,高層模塊不該該依賴低層模塊,這裏的高層模塊就是領域層,低層模塊是基礎設施層的倉儲。this
依賴倒置原則反轉了二者的依賴關係,即倉儲反過來依賴於領域層。爲了讓領域層能夠訪問到倉儲提供的服務,須要抽取倉儲接口,你能夠把這些接口放到獨立的程序集中,但這會增長沒必要要的開銷。一種更好的方法是直接把低層接口放入使用它們的客戶程序集中,這樣能夠簡化設計,僅在必要時才分離出獨立的接口程序集。依賴倒置原則不只適用於倉儲,對於任何可能致使高耦合的基礎設施服務都適用,好比第三方外部接口,發郵件,發短信等。spa
在Util.Datas.Ef項目中建立一個倉儲實現類Repository。設計
倉儲接口IRepository的代碼以下。code
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的代碼以下。htm
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類型限定爲聚合。blog
在Repository實現類中,經過注入DbContext工做單元來完成全部的工做。IUnitOfWork和EfUnitOfWork是在應用程序框架實戰十九:工做單元層超類型中定義的,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