3.2.- 使用实体框架的领域实体
EF 4.1提供了下面的选则来实现实体:
- 按照模型优点或者数据库优先的方法:
o 指令性实体(于EF的基类和实体对象模板耦合)需要使用部分类来添加实体逻辑。
o 自跟踪实体连上基于对象上下文的环境,需要使用部分类来添加实体逻辑。
o POCO实体连上基于对象上下文的环境,需要使用部分类来添加实体逻辑。
o POCO实体和EF 4.1 DbContext 生成器连上基于对象上下文的环境,需要使用部分类来添加实体逻辑。
- 按照代码优先的方法:
o POCO实体连上基于对象上下文的环境。在POCO实体类中直接添加数据属性和领域逻辑。
如果想获得使用.NET最纯净的领域驱动设计,最后的选择(代码优先和领域实体加上DTO)是最合适,
它把表现层和领域实体解耦。对于领域实体和DTO的映射,我们会手动完成。
另一方面,自跟踪实体是一个平衡的方法,由于其自跟踪数据的能力在开始阶段提供了更多效率的开发,
但是随着项目变大,会没有POCO-代码优先的灵活性更高。
请参照分发服务的章节来分析对于使用DTO还是自跟踪实体(STE)。
在本架构指南的第一个版本我们选择了STE(那时候代码优先还不可行),但在这个版本我们将关注POCO
实体和代码优先,由于这是更加面向领域驱动设计的选择。
因此,目前我们选择实现领域实体的方法是代码优先。代码优先是ADO.NET实体框架的新的开发模式,同时
也提供数据库优先和模型优先的模式。代码优先关注与使用C#/Visual Basic定义模型的类,这些类可以
映射到现存的数据库或用来生成数据库架构。额外的配置可以使用Data Annotations或API。
3.3.- 使用代码优先的方法实现POCO领域实体类
对于有着较长生命周期和很多领域逻辑变化的应用,建议使用代码优先+DTO的方法(DTO主要为了WEb服务)。
代码优先可以让我们使用C#或VB.Net的类定义模型。额外的配置可以使用Data Annotations或API。模型可以
用来生成数据库架构或映射到现存的数据库。
代码优先的一个优点是我们可以很早的关于领域实体而不需要考虑或实现数据连接和映射。这就是为什么我们
改变的本章的顺序。这是领域驱动设计的方法:”让我们先考虑领域模型“,关于我们想解决的问题,之后会映射
到具体的持久化技术。使用代码优先方法我们可以使用在更早的阶段运用POCO领域实体类。甚至可以默认的数据库
测试实体而不需要管持久化细节,感谢代码优先提供的默认转换,会在后面讨论。
下面的图说明了从领域模型开发的工作方法:
3.4.- 设置Entity Framework 4.1
由于EF4.1比.NET 4.0新,我们需要通过安装程序来安装:
http://www.microsoft.com/downloads/en/details.aspx?FamilyID=b41c728e-9b4f-4331-a1a8-537d16c6acdf&displaylang=en ),
或者使用NuGet安装。
注意:NuGet是一个在CodePlex上(http://nuget.codeplex.com) 的开源项目,它可以管理第三方的包或者库。如果
不了解它,可以使用Visual Studio 的"Extension Manager‟。
当我们安装了NuGet后,我们只需要添加“Entity Framework”的包,输入“Install-Package EntityFramework‟。
http://www.microsoft.com/downloads/en/details.aspx?FamilyID=b41c728e-9b4f-4331-a1a8-537d16c6acdf&displaylang=en ),
或者使用NuGet安装。
注意:NuGet是一个在CodePlex上(http://nuget.codeplex.com) 的开源项目,它可以管理第三方的包或者库。如果
不了解它,可以使用Visual Studio 的"Extension Manager‟。
当我们安装了NuGet后,我们只需要添加“Entity Framework”的包,输入“Install-Package EntityFramework‟。
当我们添加完”Entity Framework"包后,我们就会在项目中更新并引用了它。
3.5.- 用“代码优先”实现领域实体类
现在,我们将考虑我们的领域模型组件,需求等等,感谢代码优先,我们可以直接定义和实现领域实体类。因此,
我们可以没有基础设施层的时候“使用”我们的领域层的类。这是为什么"代码优先"比"数据库优先“或”模型优先“
更适合领域驱动设计。
在领域模型层中,我们通常会有一存放领域模型类的程序集(类库项目)。
3.5.- 用“代码优先”实现领域实体类
现在,我们将考虑我们的领域模型组件,需求等等,感谢代码优先,我们可以直接定义和实现领域实体类。因此,
我们可以没有基础设施层的时候“使用”我们的领域层的类。这是为什么"代码优先"比"数据库优先“或”模型优先“
更适合领域驱动设计。
在领域模型层中,我们通常会有一存放领域模型类的程序集(类库项目)。
在这个项目中,把表现聚合的实体组织到一个文件夹是一个很好的做法。之后我们可以直接添加POCO实体类来实现
”订单聚合“:
”订单聚合“:
//POCO Entity Class (Root Entity Class for Order-Aggregate)
public class Order : Entity, IValidatableObject
{
//Order lines set
HashSet<OrderLine> _Lines;
//Order info
public DateTime OrderDate { get; set; }
public DateTime? DeliveryDate { get; set; }
public bool IsDelivered { get; set; }
Order: POCO Root Entity Class (For „Aggregate Order‟)
public Guid CustomerId { get; set; }
public virtual Customer Customer { get; private set; }
public virtual ShippingInfo ShippingInformation { get; set; }
//...
//Ommitted other Order entity methods
//...
public void SetOrderAsDelivered()
{
this.DeliveryDate = DateTime.UtcNow;
this.IsDelivered = true;
}
public decimal GetOrderTotal()
{
decimal total = 0M;
if (OrderLines != null //use OrderLines for lazy loading
&&
OrderLines.Any())
{
total = OrderLines.Aggregate(total,
(t, l) => t += l.TotalLine);
}
return total;
}
public bool IsCreditValidForOrder()
{
//Check if amout of order is valid for the customer credit
decimal customerCredit = this.Customer.CreditLimit;
if (this.GetOrderTotal() > customerCredit)
return false;
//TODO: This is a parametrizable value, you can
//set this value in configuration or other system
decimal maxTotalOrder = 1000000M;
//Check if total order exceeds limits
if (this.GetOrderTotal() > maxTotalOrder)
return false;
return true;
}
public virtual ICollection<OrderLine> OrderLines
{
get
{
if (_Lines == null)
_Lines = new HashSet<OrderLine>();
return _Lines;
}
set
{
_Lines = new HashSet<OrderLine>(value);
}
}
Figure 13.- Order Entity Root Class
//POCO Entity Class
public class OrderLine
: Entity, IValidatableObject
{
public decimal UnitPrice { get; set; }
public int Amount { get; set; }
public decimal Discount { get; set; }
public Guid OrderId { get; set; }
public Guid ProductId { get; set; }
public Product Product { get; private set; }
public decimal TotalLine
{
get
{
return (UnitPrice * Amount) * (1 - (Discount/100M));
}
}
public void SetProduct(Product product)
{
if (product == null
||
product.IsTransient())
{
throw new ArgumentNullException(Messages.
exception_CannotAssociateTransientOrNullProduct);
}
//fix identifiers
this.ProductId = product.Id;
this.Product = product;
}
//Ommited other methods
//…
}
使用这些类不需要任何传统的EF 4.0 EDMX文件,就完成了。使用EF 4.1和代码优先可以直接创建领域实体
模型。
一旦我们有了实体模型,我们可以在内存中测试这些类甚至做更多,通过几步,就可以保存到数据库中。但是
我们会在下一章做这些。