前言:上篇介紹了下倉儲的代碼架構示例以及簡單分析了倉儲了使用優勢。本章還是繼續來完善下倉儲的設計。上章說了,倉儲的最主要作用的分離領域層和具體的技術架構,使得領域層更加專注領域邏輯。那么涉及到具體的實現的時候我們應該怎么做呢,本章就來說說倉儲里面具體細節方便的知識。
DDD領域驅動設計初探系列文章:
- C#進階系列——DDD領域驅動設計初探(一):聚合
- C#進階系列——DDD領域驅動設計初探(二):倉儲Repository(上)
- C#進階系列——DDD領域驅動設計初探(三):倉儲Repository(下)
- C#進階系列——DDD領域驅動設計初探(四):WCF搭建
- C#進階系列——DDD領域驅動設計初探(五):AutoMapper使用
- C#進階系列——DDD領域驅動設計初探(六):領域服務
- C#進階系列——DDD領域驅動設計初探(七):Web層的搭建
一、對倉儲接口以及實現基類的完善
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)模式。