上一篇中,我們分析了Common中的幾個類,這幾個類都是輔助用的,其實不太重要,重要使我們今天要分析的這幾個類,包括Entity、IDatabase、IEntityDataAccess,其中Entity作為所有實體類的基類,更是重中之重,而IDatabase、IEntityDataAccess這兩個介面,則是為Entity類服務的。
先看那個熟悉的系統結構圖。
Entity、IDatabase、IEntityDataAccess這三個類型位於Business層中。該層目前的類圖如下:
可以看到,Business層包含Blog和BlogClass這兩個實體類,他們都繼承於Entity類;每個實體類(包括Entity)都對應一個Entension類,這個類自然就是存放該類相關的Extension方法的地方,通常是對IQueryable和IEntityAccess介面的擴充;另外IDatabase和IEntityAccess兩個介面定義了資料庫訪問的方法。
一、Entity類。Entity是所有實體類的基類,是一個模板類不能是結構體的泛型類。它為所有的實體類提供了ID、TimeStamp和IsNew的屬性,Validate、Save和Delete方法,每個方法又包含實際操作ValidateAction、SaveAction和DeleteAction。
1,屬性:
ID和TimeStamp屬性是抽象屬性,之所以這樣,是因為Linq的繼承目前還不是很成熟,所以我們把ID和TimeStamp的具體資訊延遲的每個實體類(使用代碼產生),這樣,對於Linq來說,就是一個不存在繼承的實體體系。另外,ID和TimeStamp是唯讀,Linq可以通過反射設定欄位的內容,所以我們把ID和TimeStamp設定為唯讀,只由Linq負責這兩個欄位的值,防止類外部的修改,這一技巧也可以用於其它不允許類外部修改的屬性(即只包括get方法的屬性)。IsNew方法是基於ID欄位判斷的,參看以下代碼。由於ID是唯讀,所以我們可以安全的通過判斷ID是否為0來識別該欄位是new出來的還是從資料庫中讀取出來的。
1: /// <summary>
2: /// 新實體的ID取值
3: /// </summary>
4: public const int NEW_ENTITY_ID = 0;
5: /// <summary>
6: /// 取得實體是否是新實體
7: /// </summary>
8: public bool IsNew
9: {
10: get { return ID == NEW_ENTITY_ID; }
11: }
2,方法:
Entity提供了Validate、Save和Delete三個方法,每個方法又包含可以被子類重寫的實際操作:ValidateAction、SaveAction和DeleteAction。兩兩之間通常是這樣子的:
1: /// <summary>
2: /// 驗證
3: /// </summary>
4: /// <param name="database">資料庫</param>
5: /// <returns>驗證結果</returns>
6: public ValidateResult Validate(IDatabase database)
7: {
8: if (database == null)
9: throw new ArgumentNullException("database");
10:
11: var validater = new Validater();
12: ValidateAction(validater, database);
13: return validater.Validate();
14: }
15: /// <summary>
16: /// 驗證動作
17: /// </summary>
18: /// <param name="validater">驗證器</param>
19: /// <param name="database">資料庫</param>
20: protected virtual void ValidateAction(Validater validater, IDatabase database) { }
即Validate等入口方法提供了諸如驗證參數等得共同操作,而提供給子類可以重寫的Action方法。三個方法中比較特別的是Save方法,它在執行Action前,會先調用Validate方法,以保證儲存進資料庫的資料全部是合法資料。如下:
1: public void Save(IDatabase database)
2: {
3: if (database == null)
4: throw new ArgumentNullException("database");
5:
6: var validateResult = this.Validate(database);
7: if (!validateResult.IsValidated)
8: throw new ValidateFailException(validateResult);
9:
10: SaveAction(database);
11: }
Validate、Save和Delete所帶的參數都是IDatabase介面,這個介面定義了資料庫所要實現的全部操作,應該說是一個非常粗顆粒度的介面(雖然現在該介面只有一個方法)。使用粗顆粒度的介面的好處是:實體類內部在處理與自身相關的資料時,可以訪問到整個資料庫,使用起來方便。從邏輯上講,這三個方法使用資料庫作為參數,表示在該資料內容環境中,執行Validate、Save和Delete操作,意思也是很清晰的。後面會出現的對於資料庫的操作,都將以這種粗顆粒度的介面作為參數。不過使用粗顆粒度的介面也要注意,實現機制不能過於臃腫,不然構造太多的執行個體會影響系統效能,我們使用Lazy Load解決這個問題,在分析資料訪問的實現時,我們還會回到這個問題上來。
EntityExtension這個類提供了一個非常常用的擴充方法:根據ID取得實體:
1: /// <summary>
2: /// 根據ID取得實體
3: /// </summary>
4: /// <typeparam name="T">實體類型</typeparam>
5: /// <param name="query">實體查詢</param>
6: /// <param name="id">ID</param>
7: /// <returns>對應的實體,不存在則返回空</returns>
8: public static T GetByID<T>(this IQueryable<T> query, int id)
9: where T : Entity<T>
10: {
11: if (query == null)
12: throw new ArgumentNullException("query");
13:
14: return query.SingleOrDefault(entity => entity.ID == id);
15: }
有了這個方法,結合資料提供者,就可以通過這樣的方式取得一個Blog:database.GetDataAccess<Blog>().GetByID(1);
該方式,以後還會進一步簡化為:database.Blogs.GetByID(1);
今天我們分析了實體基類的代碼和設計方式,下一篇中我們將著重研究資料訪問兩個介面的設計與實現。
代碼下載