Entity Framework In Action--5 Domain model mapping(1)
5.1 The Entity Data Model
EDM是EF的心脏。本质上来说,EF是一个工具,他通过创建一个介于对象模型和数据库的抽象层来为他们解耦和。你已经知道EDM被分为了三个部分:
概念模型(CSDL)--描述对象模型。在这部分类和他们的关系有他们的副本。
存储模型(SSDL)--描述数据库结构。表、视图,甚至存储过程和函数会在这个部分。
映射模型(MSL)--映射CSDL和SSDL。
5.1.1 The Entity Data Model and Visual Studio designer
双击Solution Explorer里的EDMX文件,VS打开设计器,显示实体。右键单击EDMX,选择“Open With..”,选择"XML(Text)Editor"。
EDM在edmx:Edmx/edmx:Runtime路径下。在他里面,你可以看到存储(storage)、概念(conceptual)和映射模型。
EF不是对所有的EDMX文件感兴趣--它甚至不能解析他。EF仅仅知道被分为三部分的映射文件:csdl、ssdl、msl。
EDMX文件包含EDM,但是EF不知道他。
5.2 Creating consumable entities
1、创建实体代码。
2、创建概念模型。
3、创建存储模型。
4、创建映射模型。
5.2.1 Writing the entities
public class AddressInfo { public virtual string Address { get; set; } Public virtual string ZipCode { get; set; } Public virtual string City { get; set; } Public virtual string Country { get; set; } } public class Order { public virtual int OrderId { get; set; } public virtual DateTime OrderDate { get; set; } public virtual AddressInfo ShippingAddress { get; set; } public virtual DateTime EstimatedShippingDate { get; set; } public virtual DateTime ActualShippingDate { get; set; } } public class OrderDetail { public virtual int OrderDetailId { get; set; } public virtual int Quantity { get; set; } public virtual decimal Price { get; set; } public virtual decimal Discount { get; set; } }
当你实例化(instantiate)Order时,因为没有被创建address是null。这意味着在你创建Order的时候要创建address,那是容易重复出错的地方。你有两个选择解决这个问题:在Order构造函数内实例化address,或者在它第一次被访问的时候实例化他。
public Order() { ShippingAddress = new AddressInfo(); }
private AddressInfo _ShippingAddress; public virtual AddressInfo ShippingAddress { get { _ShippingAddress = _ShippingAddress ?? new AddressInfo(); return _ShippingAddress; } set { _ShippingAddress = value; } }
Equals和GetHashCode的重写。当你创建一个对象,为每个类实现他们很重要。
public class Order { ... public override bool Equals(object obj) { Order order = obj as Order; if (order == null) return false return order.OrderId == this.OrderId; }
public override int GetHashCode() { return OrderId.GetHashCode(); } }
这段代码说的是如果两个order的OrderId属性相同,他们相等。
一个类必须能被继承而且所有属性必须是virtual/Overridable。你可以避免这样的规则,但是被获取的对象不会提供像对象追踪和延迟加载等特性。因为在很多情况下他们是很重要的,我们建议将属性设为virtual。
5.2.2 Describing entities in the conceptual schema.
概念模型(conceptual schema)包括对实体的描述。
在OrderIT的EDMX文件里,概念模型的路径是 edmx:Edmx/ edmx:Runtime/edmx:ConceptualModels.如果你手动创建概念文件,你可以为他起名OrderIT.csdl并在数据库连接字符串引用他。
基本的csdl结构是相当简单的。他有主元素Schema,在他内部是一个EntityContainer元素,加上每个实体一个的EntityType和每个复杂类型的一个ComplexType。
Schema
Schema是一个很简单的元素。他包含了Namespace属性和Alias来区分命名空间并给他一个别名(alias)。除此之外,他通过xmlns声明了基础命名空间,并添加prefix前缀声明一个额外的命名空间。
<Schema xmlns="http://schemas.microsoft.com/ado/2008/09/edm" xmlns:store="http://schemas.microsoft.com/ado/2007/12/edm/"
Namespace="OrderITModel"
Alias="Self"> ... </Schema>
因为Schema不过(mere)是一个容器,他没有太多特别的属性。我们更感兴趣的是他的内容。
EntityContainer
EntityContainer元素声明了EDM里的实体集合关系集。这里声明的实体集用来生成上下文类的代码。这个元素只有Name属性,这个name就是设计器你在EDMX向导输入的连接字符串的名字。如果你手动的创建文件,我们建议你设定name为你的程序名,再加上后缀Entities。
注意:你可能有很多的EntityContainer标签,意味着你有很多的上下文。这在你想要有逻辑地区分你的应用的时候会很有用。设计器不支持 它,但是你可以手动编辑它。 |
正如你所料的(As you may expect),EntityContainer只是一个外包。在他里面是每一个实体集的EntitySet元素。你在第2章学到在模型(model)里,对于每个类都有一个实体集,不继承于另一个。例如,Order有一个实体集,Company有一个实体集,但是因为Supplier和Customer继承自Company,所以他们没有实体集。
EntitySet有两个属性:
Name声明实体集的唯一名字。
EntityType 包含实体公开的完全名称(fully qualified name,FQN)。
最后,实体容器看起来是下面这样的:
<EntityContainer Name="OrderITEntities"> <EntitySet Name="Orders" EntityType="OrderIT.DomainModel.Order" /> <EntitySet Name="OrderDetails"EntityType="OrderITModel.OrderDetail" /> </EntityContainer>
ComplexType And EntityType
现在你明白了EntityContainer,我们可以去看看模型类了。用来描述一个实体的元素是EntityType,而一个复杂类型用ComplexType来表示。
ComplexType只有Name属性,填入类的完全名称。在他内部为每个属性提供一个名为Property的节点。Property有很多属性,看下表:
Attribute | Description | Required |
Name | 识别属性的名称 | Yes |
Type | 识别属性的CLR类型,如果属性是复杂类型,这里是属性的完全名称。 | Yes |
Nullable | 是否属性值可以为null,默认是true | No |
FixedLength | 属性是不是固定长度的 | No |
MaxLength | 值的最大长度 | No |
Scale | 在一个decimal类型里,逗号(comma)后面有多少位 | No |
Precision | decimal有多少位 | No |
store:StoreGeneratedPattern |
在插入和更新的时候,列是如何被数据库设置的,三个可能的值: None-从应用程序来的值被用。 Identity-插入的时候被数据库计算,更新的时候用应用程序的值。 Computed-在插入和更新的时候都被数据库计算。 |
No |
ConcurrencyMode(并发模式) | 属性是否做并发检查,执行检查,设置值为Fixed | No |
当复杂类型准备好的时候,你就可以用它来创建实体了。EntityType是你做这个的地方。他有一个强制性(mandatory)的Name属性,类的名称被指定的。他有两个选择属性:
Abstract-是不是抽象类。
BaseType-是不是基类。
当继承起作用的时候,Abstract和BaseType就变的重要了。在EntityType里,每个scalar和复杂属性都有一个Property元素,和一个Key元素来识别主键。如果属性是复杂类型,设定Type属性为完全名称。Key元素没有属性,他有一个PropertyRef节点来表示主键。PropertyRef只有Name属性。
<ComplexType Name="AddressInfo"> <Property Type="String" Name="Address" Nullable="false" MaxLength="50" /> <Property Type="String" Name="City" Nullable="false" MaxLength="50" /> <Property Type="String" Name="ZipCode" Nullable="false" MaxLength="15" /> <Property Type="String" Name="Country" Nullable="false" MaxLength="30" /> </ComplexType> <EntityType Name="Order"> <Key> <PropertyRef Name="OrderId" /> </Key> <Property Type="Int32" Name="OrderId" Nullable="false" store:StoreGeneratedPattern="Identity" /> <Property Name="ShippingAddress" Type="OrderITModel.AddressInfo" Nullable="false" /> <Property Type="DateTime" Name="EstimatedShippingDate" Nullable="false" Precision="29" /> <Property Type="DateTime" Name="ActualShippingDate" Nullable="false" Precision="29" /> </EntityType> <EntityType Name="OrderDetail"> <Key> <PropertyRef Name=" OrderDetail Id" /> </Key> <Property Type="Int32" Name="OrderDetailId" Nullable="false" store:StoreGeneratedPattern="Identity" /> <Property Type="Int16" Name="Quantity" Nullable="false" /> <Property Type="Decimal" Name="UnitPrice" Nullable="false" Precision="29" Scale="29" /> <Property Type="Decimal" Name="Discount" Nullable="false" Precision="29" Scale="29" /> </EntityType>