首先分享一點自己最近的感悟:討厭你的人總可以找到理由去討厭你
正文開始
如果您是初次閱讀這個系列,請先去《Index & Writing Plan》查找並閱讀“架構設計系列”的前兩篇文章,順序閱讀會使您有更好的閱讀體驗
強烈推薦配合源代碼閱讀本文:點擊此處下載(可以直接運行,會在本地自動生成數據庫)
已經寫完了Factory的實現。在Factory中,我們使用了預編譯指令來實現了Model的切換:
#define A #if B using Model.B; using DBaccess.B; #endif #if A using Model.A; using DBaccess.A; #endif
切換Model,只需將#define A 修改為 #define B,非常方便(盡管要重新編譯,還是讓我有點不爽)
但是出現預編譯指令的地方不宜過多——事實上,已經有一處要改就已經讓我不爽了
所以,在DM層(數據操作層)與Service層(業務邏輯層)中,我們不能出現任何具體的Model的類名。
在本例中,就是DM和Service中不能出現類名:Teacher、Contact
因為每個出現了類Teacher和類Contact的地方,我們都要加上前文提到的預編譯指令。而在實際項目中,這樣做會導致切換Model要修改的地方非常多,可能會導致不可預期的錯誤
那么,DM層主要是用泛型來解決問題,代碼如下:
public class DMbase { protected DbContext db; public DMbase(DbContext db) { this.db = db; } /// <summary> /// select one /// </summary> /// <typeparam name="T"></typeparam> /// <param name="entity">這里的entity並無實際作用,只是用於編譯器推敲類型</param> /// <param name="predicate">λ表達式</param> /// <returns>返回第一條匹配的記錄,若無記錄返回null</returns> public virtual T FindOne<T>(T entity, Func<T, bool> expression) where T : class, new() { return this.db.Set<T>().FirstOrDefault(expression); } /// <summary> /// select all /// </summary> /// <typeparam name="T"></typeparam> /// <param name="entity">這里的entity並無實際作用,只是用於編譯器推敲類型</param> public virtual IQueryable<T> FindAll<T>(T entity) where T : class, new() { return this.db.Set<T>(); } public virtual void Insert<T>(params T[] entities) where T : class, new() { if (entities != null && entities.Length > 0) { var set = this.db.Set<T>(); foreach (var item in entities) { set.Add(item); } } } public virtual void Delete<T>(params T[] entities) where T : class, new() { if (entities != null && entities.Length > 0) { var set = this.db.Set<T>(); foreach (var item in entities) { set.Remove(item); } } } /// <summary> /// 提交事務 /// </summary> public void Commit() { this.db.SaveChanges(); } }
我的注釋也說明了,其實FindOne方法和FindAll方法其實是不需要參數的,但是為了編譯器推敲類型,傳入了一個entity參數
在此要感謝下xanthodont同學,這個DM是在你的版本上改的,封裝得很不錯
寫完DM層,接下來就是重頭戲,Service層
下面的代碼示范了如何取得所有的Teacher,並將Teacher與Contact對應起來
public object FindAllTeacher() { //取得一個Context var context = ContextFactory.GetContext(); //從Factory中取得Teacher和Contact //這里必須要用var var a = ModelFactory.GetTeacher(); var b = ModelFactory.GetContact(); var aList = ModelListFactory.GetTeacherList(); DMbase dm = new DMbase(context); //用之前取得的Teacher與Contact傳入DM層,方便編譯器推敲類型 var contact = dm.FindAll(b); var teacher = dm.FindAll(a); //業務邏輯,將Teacher與Contact關聯起來 var result = teacher.Join(contact, o => o.ID, r => r.TeacherID, (o, r) => new { tt = o, aa = r }); result = result.OrderByDescending(o => o.aa.Email); //處理數據 foreach (var item in result) { a = item.tt; a.Contact = item.aa; aList.Add(a); } return aList; }
注意,這里我使用了Object作為返回類型,因為我其實不知道返回值是IList<Model.A.Teacher> 還是IList<Model.B.Teacher>,好在這里只需要一次拆裝箱,無傷大雅
再來演示下如何插入:
public bool AddTeacher<T, T1>(T teacher, T1 contact) where T : class,new() where T1 : class,new() { try { var context = ContextFactory.GetContext(); DMbase dm = new DMbase(context); dm.Insert(teacher); dm.Insert(contact); dm.Commit(); return true; } catch { return false; } }
只有執行了dm.Commit(),兩個Insert才會提交到數據庫,保證了事務的一致性
就此擱筆
PS:昨天是博主生日,又老了一歲;有緣讀到這里的園友,就別吝嗇自己的祝福了吧 :)