【转】EntityFramework之领域驱动设计实践(三)

原文地址:http: www cnblogs com daxnet archive 2010 07 07 1772593 html 案例:一个简易的销售系统

原文地址:http://www.cnblogs.com/daxnet/archive/2010/07/07/1772593.html

 

案例:一个简易的销售系统

从现在开始,我们将以一个简易的销售系统为例,探讨EntityFramework在领域驱动设计上的应用。为了方便讨论,我们的销售系统非常简 单,不会涉及客户存在多个收货地址的情况,也不会包含任何库存管理的内容。假设我们的系统只需要维护产品类型、产品以及客户信息,并能够帮客户下订单、跟 踪订单状态,以及接受客户退货。从简单的分析我们大致可以了解到,这个系统将会有如下实体:客户、单据、产品及其类型。单据分为销售订单和退货单两种,每 个单据可以有多个单据行(比如销售订单行和退货单行)。不仅如此,系统允许每个客户有多张信用卡,以便在结账的时候,选择一张信用卡进行支付。在使用EF 的Entity Data Model Designer进行设计后,我们得到下面的模型:

71531210423

上面的模型表述了领域模型中各个实体及其之间的关系。我们先不去讨论整个系统的业务会是什么样的,我们先看看EF是如何支持实体和值对象概念的。

 

实体

首先看看实体这个概念。在领域驱动设计的理论中,实体是模型中需要区分个体的对象,也就是说,针对某种类型,我们既要知道它是什么,还需要知道它是 哪个。我在前面的博文中有介绍过实体这个概念。实体都有一个标识符,以便跟同类型的其它实体进行区分。EF Entity Data Model Designer上能够画出的都是实体,你可以看到每个实体都有个Id成员,其Entity Key属性被设置为True,同时被分配了一种标识符的生成方式,如下图所示:

71541799262

在从领域模型映射到数据模型的过程中,这个标识符通常都是被映射为关系数据库某个数据表的主键,这个应该是很容易理解的。

其次,EF不支持实体行为,因此,整个模型只能被称为Entity Data Model,而不是Entity Model,因为它只支持对实体数据的描述。幸亏从.NET 2.0开始,托管语言开始支持partial特性,同一个类可以以部分类(partial class)的特性写入多个代码文件中。因此,如果需要向上述模型中的实体加入行为,我们可以在工程中加入几个代码文件,然后使用部分类的特点,为实体添 加必要的行为。比如,下面的部分类向订单行中加入了一个只读属性,该属性用于计算某一单据行所拥有的总金额:

71555271864

有朋友会问,为什么我们要另外使用部分类,而不是直接在模型文件 edmx的源代码上直接修改?因为这个源代码文件是框架动态生成的,如果在上面修改,等下次模型被更新的时候,你所做的更改便会丢失。

对于实体的行为,EF支持从数据库存储过程生成实体对象行为的过程。对此,我持批判态度:EF把数据模型与实体模型混为一谈了,这种做法只能让软件人员感到更加困惑。我在下一篇文章将重点表述我对这个问题的看法。我也相信微软在下一代实体框架中能够处理好这个问题。

再次,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. }

 

对于值对象的两点问题我在第一篇文章中已经讲过了,在此就不重复了。

综上所述,EF基本上能够支持领域驱动设计的思想(即使有些方面不完善,但目前也可以找到替代的方案)。我想,只要能够对领域驱动有清晰的认识,就能够很好地将实体框架应用于领域驱动的实践中。