EntityFramework之領域驅動設計實踐(三)

來源:互聯網
上載者:User
案例:一個簡易的銷售系統

從現在開始,我們將以一個簡易的銷售系統為例,探討EntityFramework在領域驅動設計上的應用。為了方便討論,我們的銷售系統非常簡單,不會涉及客戶存在多個收貨地址的情況,也不會包含任何庫存管理的內容。假設我們的系統只需要維護產品類型、產品以及客戶資訊,並能夠幫客戶下訂單、跟蹤訂單狀態,以及接受客戶退貨。從簡單的分析我們大致可以瞭解到,這個系統將會有如下實體:客戶、單據、產品及其類型。單據分為銷售訂單和退貨單兩種,每個單據可以有多個單據行(比如銷售訂單行和退貨單行)。不僅如此,系統允許每個客戶有多張信用卡,以便在結賬的時候,選擇一張信用卡進行支付。在使用EF的Entity Data Model Designer進行設計後,我們得到下面的模型:

上面的模型表述了領域模型中各個實體及其之間的關係。我們先不去討論整個系統的業務會是什麼樣的,我們先看看EF是如何支援實體和值對象概念的。

 

實體

首先看看實體這個概念。在領域驅動設計的理論中,實體是模型中需要區分個體的對象,也就是說,針對某種類型,我們既要知道它是什麼,還需要知道它是哪個。我在前面的博文中有介紹過實體這個概念。實體都有一個標識符,以便跟同類型的其它實體進行區分。EF Entity Data Model Designer上能夠畫出的都是實體,你可以看到每個實體都有個Id成員,其Entity Key屬性被設定為True,同時被分配了一種標識符的產生方式,如所示:

在從領域模型映射到資料模型的過程中,這個標識符通常都是被映射為關聯式資料庫某個資料表的主鍵,這個應該是很容易理解的。

其次,EF不支援實體行為,因此,整個模型只能被稱為Entity Data Model,而不是Entity Model,因為它只支援對實體資料的描述。幸虧從.NET 2.0開始,託管語言開始支援partial特性,同一個類可以以部分類(partial class)的特性寫入多個代碼檔案中。因此,如果需要向上述模型中的實體加入行為,我們可以在工程中加入幾個代碼檔案,然後使用部分類的特點,為實體添加必要的行為。比如,下面的部分類向訂單行中加入了一個唯讀屬性,該屬性用於計算某一單據行所擁有的總金額:

有朋友會問,為什麼我們要另外使用部分類,而不是直接在模型檔案 edmx的原始碼上直接修改?因為這個原始碼檔案是架構動態產生的,如果在上面修改,等下次模型被更新的時候,你所做的更改便會丟失。

對於實體的行為,EF支援從資料庫預存程序產生實體物件行為的過程。對此,我持批判態度:EF把資料模型與實體模型混為一談了,這種做法只能讓軟體人員感到更加困惑。我在下一篇文章將重點表述我對這個問題的看法。我也相信微軟在下一代Entity Framework中能夠處理好這個問題。

再次,EF對實體物件關係的支援主要有關聯和繼承。根據Multiplicity的設定,關聯又可以支援到組合關聯與彙總關聯。我覺得EF中對繼承關係的支援是一個亮點。繼承表述了“什麼是一種什麼”的觀點,比如在我們的案例中,“銷售訂單”和“退貨單”都是一種“單據”。如果從傳統的資料庫驅動的設計方案,我們很自然地會使用“Orders”資料表中的整型欄位“OrderType”來儲存當前單據的類型(比如0表示銷售訂單,1表示退貨單),那麼,在擷取系統中所有銷售訂單的時候,我們會使用下面的代碼:

隱藏行號 複製代碼 ? 擷取所有銷售訂單的虛擬碼
  1. List<Order> GetSalesOrders(IDbConnection connection)
  2. {
  3.     IDbCommand command = new SqlCommand("SELECT * FROM [Orders] WHERE [OrderType]=0",
  4.         (SqlConnection)connection);
  5.     List<Order> orders = new List<Order>();
  6.     using (IDataReader dr = command.ExecuteReader())
  7.     {
  8.         while (dr.Read())
  9.         {
  10.             Order order = new Order();
  11.             order.Id = Convert.ToInt32(dr["Id"]);
  12.             // ...
  13.             orders.Add(order);
  14.         }
  15.         dr.Close();
  16.     }
  17.     return orders;
  18. }

從技術角度講,上面的代碼沒什麼問題,啟動並執行也很好,能夠獲得系統中所有銷售訂單的列表。但是, [OrderType]=0這種寫法並不包含任何領域語義,如果讓另一個開發人員來跟進這段代碼,他不得不先去查閱其它項目文檔,以瞭解這個 [OrderType]=0的具體涵義。在引入了繼承關係的EF中,我們只需要下面的Linq to Entities,即可既方便、又明了地獲得所有銷售訂單的列表了:

隱藏行號 複製代碼 ? 使用LINQ to Entities後的代碼
  1. List<Order> GetSalesOrders()
  2. {
  3.     using (EntitiesContainer container = new EntitiesContainer())
  4.     {
  5.         return (from order in container.Orders
  6.                 where order is SalesOrder
  7.                 select order).ToList();
  8.     }
  9. }

簡單明了吧?EF帶給我們的不僅僅是一個技術架構,也不僅僅是一個資料存取的解決方案。

 

值對象

EF支援值對象,這很好!在EF中可以定義Complex Types,而一個Complex Type下可以定義多個Primitive Type和多個Complex Type。與LINQ to SQL相比,這是一大進步。

 

 

對於值對象的兩點問題我在第一篇文章中已經講過了,在此就不重複了。

綜上所述,EF基本上能夠支援領域驅動設計的思想(即使有些方面不完善,但目前也可以找到替代的方案)。我想,只要能夠對領域驅動有清晰的認識,就能夠很好地將Entity Framework應用於領域驅動的實踐中。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.