標籤:style blog http color os 使用 strong ar for
上次趕時間,就很流水賬地寫了上次項目對EF的一次實踐應用模式,因為太長了,也沒能探討太多,所以再繼續擴充。
這次想探討的是,實體,如果作為類似於領域模型的業務模型存在,它的資料能否來自不同的資料來源。這個想法首先是來自於這次應用中,Model First + 代碼補充的方式形成了一個極好的效果。一方面滿足了範式,減少了資料存放區量;另一方面利用了封裝特性,向業務提供了一個符合業務期望的實體。
首先看一個例子
我用Model First建立了Account一個實體,實際產生的程式碼如下:
public partial class Account{ public int Id { get; set; } public string FirstName { get; set; } public string LastName { get; set; }}
對應地,上面三個欄位分別對應著資料庫中的三個欄位。
然後,我再用Partial Class的方式,為Account進行擴充:
public partial class Account{ public string FullName {
get
{ if (this._fullName == null) { this._fullName = String.Format("{0} {1}", this.FirstName, this.LastName); } return this._fullName;
} } string _fullName;}
這裡就是關鍵了。此時,商務邏輯拿到的稱之為Account的就不再是簡單的DAO對象了,而是一個有著四個業務所需欄位的業務模型對象。有興趣的可以去看看我上一篇《Entity Framework 與 物件導向》中間設計模式的部分,裡面講到了為實體應用上了繼承、多態、封裝等物件導向的設計方式以達到業務模型所需的效果。
我的問題
這種為Entity添加完整物件導向特性的方式,有著一種想法,那就是能否讓一個Entity完全抽象架空於儲存層,允許一個Entity的資料來自不同的資料來源。
首先,如果在儲存層的視角來看,樣本中Account的三個欄位Id、FirstName、LastName來自於關聯式資料庫,而FullName來自於計算值,也就是記憶體。站在資料庫的角度上來說,FullName確實是不符合範式,所以很“多餘”。
如果從時間軸來說,在執行個體化的時候,前三個資料庫欄位的資料就已經被載入了,而FullName是Lazy Load。
但是,對於業務來說,他獲得的不過是一個包含四個欄位的Account的執行個體。
那麼我的問題就繼續擴充了,一個業務模型,能不能將資料來源完全封裝,在業務獲得該實體的時候,完全脫離關注實現?
當我獲得一個產品,那麼這個產品的基本屬性來自關聯式資料庫,產品介紹來自NoSql,統計值來自動態計算。重要的是,這要是一個對象,而不是自行用多個對象拼裝出來的。
實際上,是封裝的問題
實際上,來到這裡,就已經很明朗,這是一個封裝的問題。我們從前遇到的是關聯式資料庫和物件導向的阻抗失配,然後用ORM解決了。而今天,我們還可能遇到非強型別對象和非關聯式資料庫以及強型別對象和關聯式資料庫混合使用的問題,而且這使得其成為常態。
通常,因為一個特性的原因,我們要在技術上進行選型。要麼用靜態語言,要麼用動態語言。但是當出現一個用另一種方式更合適的時候,實現就變得很彆扭了。
比如用上面產品的問題,多少人是把產品詳情作為整個文本值存在一個欄位裡呢?多少人又在用一張表來解決動態欄位的問題呢?
實現,現實嗎
在Account的例子中,這種行為很容易實現。一方面依託於EF提供了相關的支援,另一方面是兩個儲存源具有時間上的關聯。在進行一項業務的時候,所有資料庫裡,也就是儲存在持久化儲存空間內的資料,最終都要讀取到記憶體中才能被操作。這種縱向的關係,方面了應該來自於記憶體的資料來源在產生資料的行為嵌入到整個業務過程中。
但是當兩個資料來源沒有縱向關係而是平行關係的時候,問題就來了。這個狀況就跟當年的阻抗失配的問題類似。
首先是一個迭代的問題。
大家都知道,擷取資料是有擷取成本的。資料在抽象層面是“擷取”,在實現層面是一次“拷貝”,拷貝的過程包括了“傳輸”。拷貝和傳輸就是資料擷取的成本,這個成本在一些情形可以被很大的放大,比如從前的遞迴查詢。
如果不把遞迴寫成SQL,那麼在業務層面進行解決的話,就要往返很多次資料庫擷取資料。產生的業務結果是一樣的,但產生的效能結果差異極大。
而我假象的擷取資料的行為是這樣的:
可以簡單當兩個資料來源的時候,會存在一個“資料匯合”的問題,其實就是一個同步和匹配問題。
兩個資料來源擷取到的資料需要匹配上,在一個大量查詢的時候就變得很麻煩了。如果按照順序進行匹配不就簡單了?但是要知道,“順序”是個很奇怪的命題,你尋找到的資料是一個集合或者列表才有“順序”,對於NoSQL一類Key-Value的形態,如何定義順序呢?
另外,如果是要一個資料來源強行配合另一個資料來源,那麼一次資料擷取行為的效率,就是由最慢的一個資料來源決定的。
我再是想一下,如果是全部或者部分消極式載入,那麼是不是說,讀取資料都需要是非同步,而且合并資料的情況下,需要有一個用以合并資料的線程。
我打算嘗試
實際會遇到什麼情況不好說,所以我倒是樂於去嘗試一下。
一開始的思路,是先嘗試繼續用擴充欄位,然後把資料來源擷取封裝好;然後我應該嘗試去切入LinQ Provider,看看能不能在LinQ中下手,這對於.NET的使用或許更重要。
從EF的使用中探討業務模型能否脫離單一儲存層完全抽象存在