一直都想寫博客,可惜真的太懶了或者對自己的描述水平不太自信,所以。。。一直都是不想寫的狀態,關於領域驅動的東西看了不少,但是由於自己水平太差加上工作中實在用不到,所以一直處於擱置狀態,最近心血來潮突然想重新寫一個自己的項目架構,於是開始了新一論的學習歷程。
在設計之前我理了一下自己的大致需求也參考了不同人的項目架構,在此特別感謝郭明峰的OSharp給我的啟示,每個人對架構的需求和使用習慣都是不一至的,在不同大小的項目上使用的架構也不盡相同,如何取舍性能、開發速度及維護性的問題上每開發者都在自己心中有一桿秤,下面談談我理想中的架構,它應該提供一些常用的操作封裝及各種工具方便實現多個功能,他應該包括數據操作的方便處理,他應該提供日志及緩存的處理功能,它的每種實現是自己換的但又得具有默認值讓我在做私單的時候更快捷方便,基於以上特點於是總結了如下模塊:
1,工具庫:提供了各種常用操作,如字符串截取,各種驗證之類的helper類
2,核心庫:提供緩存日志數據的接口及緩存日志的基礎實現
3,WEB庫:針對網站相關的擴展及操作封裝
4,MVC庫:針對MVC網站的相關擴展及操作封裝
5,基礎數據庫模塊實現庫(如:EF)
回到每個架構的重點:數據訪問模塊上來,現在比較流行使用EF+ IUnitOfWork+Repository來實現數據的處理,誠然這種方式讓每一個模塊相互的依賴變得更小,更方便測試,更XXX,但是在我的實踐中,還是發現他真的麻煩,在我做一個中小項目的時候,添加一個普通的表不得不去添加實體,實體配置,倉儲接口,倉儲實現,服務接口,服務實現,controller viewmodel,view一系列下來即便是拷貝也是非常慢,即使我們可以用代碼生成器,我還是覺得太麻煩了。當然Repository模式的反對者也不少,作為一種數據操作的隔離手段,我個人覺得Repository還是很有必要存在的,經過各種取舍,最后自己使用了一種折中的方式來使用Repository,我的方式是使用IStore來代替IUnitOfWork,本質是一樣的,只是IStore實現的功能要多一些,同時使用了IStore使得我們可以很容易的替換我們的數據庫功能實現,如下圖:
由於一般的項目不需要一人寫定義一人寫實現,所以直接省去了IUserRepository及IUserService這種並不會修改實現的接口,當然如果有團隊共同開發的時候使用也是完全沒有問題的,有了以上的圖就開以開始實現代碼了:
IDataStore:
1 /// <summary> 2 /// 數據存儲器接口 3 /// </summary> 4 public interface IStore : IDisposable 5 { 6 #region UnitOfWork 7 /// <summary> 8 /// 獲取或設置 是否開啟事務提交 9 /// </summary> 10 bool TransactionEnabled { get; set; } 11 /// <summary> 12 /// 提交操作 13 /// </summary> 14 bool Commit(); 15 /// <summary> 16 /// 異步提交 17 /// </summary> 18 Task<bool> CommitAsync(); 19 #endregion 20 21 #region 基礎操作 22 /// <summary> 23 /// 添加對像 24 /// </summary> 25 void Add<TEntity>(TEntity entity) where TEntity : class; 26 /// <summary> 27 /// 更新對像 28 /// </summary> 29 void Update<TEntity>(TEntity entity) where TEntity : class; 30 /// <summary> 31 /// 刪除對像 32 /// </summary> 33 void Remove<TEntity>(TEntity entity) where TEntity : class; 34 #endregion 35 36 #region 查詢操作 37 /// <summary> 38 /// 獲取數據集查詢對像 39 /// </summary> 40 IQueryable<TEntity> GetQueryEntities<TEntity, TKey>() where TEntity : class; 41 #endregion 42 }
IRepository:
/// <summary> /// 倉儲接口 /// </summary> /// <typeparam name="TEntity">實體類型</typeparam> /// <typeparam name="TKey">實體主鍵類型</typeparam> public interface IRepository<TEntity, TKey> { #region 屬性 /// <summary> /// 獲取當前數據存儲器 /// </summary> IStore DataStore { get; } /// <summary> /// 獲取當前實體的查詢數據集 /// </summary> IQueryable<TEntity> Entities { get; } #endregion #region 基礎操作 /// <summary> /// 添加實體到倉庫 /// </summary> void Add(TEntity entity); /// <summary> /// 更新倉庫中的實體 /// </summary> void Update(TEntity entity); /// <summary> /// 從倉庫刪除指定key的實體 /// </summary> void Remove(TEntity entity); #endregion #region 查詢 /// <summary> /// 獲取指定key的實體 /// </summary> TEntity FindByKey(TKey key); #endregion }
RepositroyBase:
1 /// <summary> 2 /// 倉儲基類 3 /// </summary> 4 public abstract class RepositoryBase<TEntity, TKey> : IRepository<TEntity, TKey> 5 where TEntity : EntityBase<TKey>, new() 6 { 7 8 public IStore DataStore { get; set; } 9 10 public IQueryable<TEntity> Entities 11 { 12 get { return this.DataStore.GetQueryEntities<TEntity, TKey>(); } 13 } 14 15 public void Add(TEntity entity) 16 { 17 this.DataStore.Add<TEntity>(entity); 18 } 19 20 public void Update(TEntity entity) 21 { 22 this.DataStore.Update<TEntity>(entity); 23 } 24 25 public void Remove(TEntity entity) 26 { 27 this.DataStore.Remove<TEntity>(entity); 28 } 29 30 public TEntity FindByKey(TKey key) 31 { 32 return this.Entities.Where(t => t.Id.Equals(key)).SingleOrDefault(); 33 } 34 }
當然BaseEntity這種東西就看自己愛好添加了,按key刪除,按條件更新的方法這里就不寫了
,另外關於返回Repository返回IEnumerable還是IQueryable這個問題我個人認為如果不按照DDD的的標准來看,一個倉庫每次拿你想要的東西還是給你一個通道你想拿多少就拿多少本質上都是可以的,不過對於隔離開發人員直接操作數據確實存在隱患,不過誰叫他簡單呢?看完定義,然后如果要使用EF做為ORM來操作數據庫或者任意繼承至IDataStore的庫來完成數據操作,不過這里要自己寫的話,解析表達式還是有點困難的。。貼下簡單EF基類的簡單實現:
1 /// <summary> 2 /// EF數據存儲器(抽象類) 3 /// </summary> 4 public abstract class EFDataStore : DbContext, IStore 5 { 6 #region DbContext 7 protected EFDataStore() { } 8 protected EFDataStore(DbCompiledModel model) : base(model) { } 9 public EFDataStore(string nameOrConnectionString) : base(nameOrConnectionString) { } 10 public EFDataStore(DbConnection existingConnection, bool contextOwnsConnection) : base(existingConnection, contextOwnsConnection) { } 11 public EFDataStore(ObjectContext objectContext, bool dbContextOwnsObjectContext) : base(objectContext, dbContextOwnsObjectContext) { } 12 public EFDataStore(string nameOrConnectionString, DbCompiledModel model) : base(nameOrConnectionString, model) { } 13 public EFDataStore(DbConnection existingConnection, DbCompiledModel model, bool contextOwnsConnection) : base(existingConnection, model, contextOwnsConnection) { } 14 /// <summary> 15 /// 實體映射集合 16 /// </summary> 17 public IEnumerable<IEntityMapper> EntityConfigurations { get; set; } 18 19 protected override void OnModelCreating(DbModelBuilder modelBuilder) 20 { 21 //foreach (var mapper in EntityConfigurations) 22 //{ 23 // mapper.RegistTo(modelBuilder.Configurations); 24 //} 25 } 26 #endregion 27 28 public bool TransactionEnabled { get; set; } 29 30 public IQueryable<TEntity> GetQueryEntities<TEntity, TKey>() where TEntity : class 31 { 32 return this.Set<TEntity>(); 33 } 34 35 public bool Commit() 36 { 37 var r = this.SaveChanges(); 38 //this.Dispose(); 39 return r > 0; 40 } 41 42 public async Task<bool> CommitAsync() 43 { 44 return (await this.SaveChangesAsync()) > 0; 45 } 46 47 public void Add<TEntity>(TEntity entity) where TEntity : class 48 { 49 this.Set<TEntity>().Add(entity); 50 } 51 52 public void Update<TEntity>(TEntity entity) where TEntity : class 53 { 54 this.Entry(entity).State = EntityState.Modified; 55 } 56 57 public void Remove<TEntity>(TEntity entity) where TEntity : class 58 { 59 this.Entry(entity).State = EntityState.Deleted; 60 } 61 }
這。寫着寫着自己都有點不知所雲了,希望對正在學習階段的朋友有一點點幫助!