博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Entity Framework 4 in Action读书笔记——第五章:域模型映射(Domain model mapping)(一)...
阅读量:6830 次
发布时间:2019-06-26

本文共 9437 字,大约阅读时间需要 31 分钟。

本章内容包括:
  • 介绍EDM(Entity Data Model)
  • 创建EF域模型类
  • 描述类
  • 描述数据库
  • 映射类到数据库

首先,让我们讨论一下EDM。

Entity Data Model

Entity Data Model(EDM)是EF的核心。事实上,EF就是在对象模型(Ojbect Model)和数据库之间创建一个抽象层,用来降低两者的耦合度的工具。应用程序只与对象模型类发生作用而忽略数据库的存在,正是EDM才使降耦成为可能。

你已经知道了EDM可以分为三部分:

  • 概念模式(CSDL)——描述对象模型。
    存储模式(SSDL)——描述数据库结构。
    映射模式(MSL)——映射CSDL和SSDL。

下面让我们看一下EDM在哪里吧!

在Solution Explorer面板中,你直接双击EDMX文件,Visual Studio会以设计器视图打开。我们还可以用另一种方式打开,在EDMX文件上右击,选择“Open With”,在弹出的“Open With”对话框中选择“XML(Text) Editor”,然后在点击“OK”。

下面你就可以看到折叠的EDMX文件了:

EDM在edmx:Edmx/edmx:Runtime路径下。在它里面可以看到存储模式(storage schemas),概念模式(conceptual schemas)和映射模式(mapping schemas)。与设计器相关的信息存储在edmx:Edmx/edmx:Designer路径下。

EF对EDMX文件并不感兴趣,它只能理解EDMX分解成的三部分:概念模式(*.csdl),存储模式(*.ssdl),映射模式(*.msl)。

如果查看连接字符串的metadata属性会发现有对这三个文件的引用:

metadata=res://*/Model.csdl|res://*/Model.ssdl|res://*/Model.msl

EDMX文件包含EDM,但是EF并不理解它。连接字符串引用在项目中任何位置的三个映射文件:这是怎么工作的呢?答案是在编译时,设计器从EDMX文件中抽取节点,并未每一个节点创建一个单独的文件。

这些文件或者是嵌入到程序集中或者复制到输出目录。你可以通过打开设计器属性,设置“Metadata Artifact Processing”属性为“Embed in Output Assembly”或者“Copy to Output Directory”选择是嵌入到程序集还是复制到输出目录。

现在你已经了解了EDM的基础知识以及如何与Visual Studio集成在一起的,是时候创建和映射Models了。创建Model,你需要做的第一件事是设计实体(Entites)。

创建实体

设计器生成代码创建实体会遵循如下步骤:

  1. 创建实体代码。
  2. 创建概念模式(conceptual schema)
  3. 创建存储模式(storage schema)
  4. 创建映射模式(mapping schema)

在下面几节,你将手动执行这些步骤,看看映射是如何工作的。

创建实体代码

在OrderIT中的订单过程包括Order和OrderDetail实体加上AddressInfo复杂类型(complex type)。暂时,我们先不考虑关联,后面再讨论。

创建订单方案模型
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; }}

在这里,复杂属性应该注意一下。当实例化Order时,ShippingAddress是null,因为它没有被创建。这也就意味着你每次创建一个Order就要创建一次ShippingAddress,这样会产生重复和易出错的代码。

有两个选择可以解决这个问题:1.在Order的构造函数中,实例化ShippingAddress;2.第一次访问时延迟实例化它。

实例化复杂属性的两种方式
//构造函数实例化public Order(){    ShippingAddress = new AddressInfo();}//延迟实例化private AddressInfo _ShippingAddress;public virtual AddressInfo ShippingAddress{    get    {        _ShippingAddress = _ShippingAddress ?? new AddressInfo();        return _ShippingAddress;    }    set    {        _ShippingAddress = value;    }}

这两种方法的结果是一样的,选择哪一个由你自己决定。

还有一个步骤我们跳过了:重写Equals和GetHashCode方法。当你创建一个模型,为每个类重写这两个方法是很重要的。

在Order实体中实现Equals和GetHashCode
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属性是相同的就相等。

在这一节中创建的类还不能被EF使用,你必须在EDM中放入必需的信息,完成这个工作的第一步就是概念(conceptual)文件。

在概念模式(conceptual schema)中描述实体(Entities)

概念模式(conceptual schema)包含对实体的描述。创建这个模式一点也不难,但是由于牵涉很多XML标签,刚开始你可能会感到困惑。幸运的是,这些标签在存储模式(storage schema)中被重用,大大地简化了整个EDM。

在OrderIT的EDMX文件中,概念模式(conceptual schema)的路径是edmx:Edmx/edmx:Runtime/edmx:ConceptualModels。如果你手动创建概念文件(conceptual file),可以将它命名为OrderIT.csdl并且在数据库连接字符串中引用它。在CSDL文件中,你必须忽略前面的XML路径,因为它是EDMX的一部分,这在存储和映射文件中也是一样的。

CSDL的基础结构很简单。它有一个主要元素(main element)Schema,在它里面有一个EntityContainer元素加上每一个实体的EntityType和每一个复杂类型的ConplexType。

对它的结构具有广泛的理解当然是好的,但了解每个节点是掌握映射必须的。下面从最外层的Schema开始,一个一个的分析它们。

SCHEMA

Schema是非常简单的元素。它包含的特性(attribute)Namespace和Alias指定它的命名空间和别名。

因为Schema仅仅是个容器,没有什么特殊的特性。

ENTITYCONTAINER

EntityContainer元素在EDM中声明实体集合和关系集合。在这里声明的实体集合用来生成上下文类的代码。该元素有两个特性,一个是Name,设计器设置它的值为你在EDMX向导中输入的连接字符串名称。如果你手动创建该文件,我们建议设置该特性的值为应用程序的名字加上Entities。另一个是批注特性annotation:LazyLoadingEnabled,默认情况下,annotation:LazyLoadingEnabled 值设置为true。

EntityContainer里面是EntitySet。在第二章中已经知道了在模型中不存在继承自另一个的实体集。例如Order有一个实体集,Company有一个实体集,但是Supplier和Customer没有,因为它们继承自Company。

EntitySet有两个特性:

  • Name——声明实体集唯一的名字。
  • EntityType——包含实体的完全限定名(FQN)。

最后,EntityContainer看起来向下面的清单:

在EntityContainer中定义实体集

COMPLEXTYPE AND ENTITYTYPE

ComplexType仅有一个Name特性,它设置为类的完全限定名(FQN)。在它内部是Property节点,Property有一些特性,如下:

特性(Attribute)

描述(Description)

Required

Name 指定属性的名称 Yes
Type 指定属性的CLR类型,如果属性是复杂类型,应该设置为复杂类型的FQN Yes
Nullable 指定属性是否可为空,默认值是true。 No
FixedLength 指定该属性是否必须是固定长度 No
MaxLength 指定值的最大长度 No
Scale 指定在decimal类型中,允许逗号后面多少位 No
Precision 指定decimal类型中允许多少位数字 No
store:StoreGeneratedPattern

指定在插入更新时数据库是怎样设置列的。有三个可选值:

  • None——使用应用程序的值(默认)
  • Identity——在插入时由数据库计算,应用程序的值在更新时使用
  • Computed——在插入和更新时都由数据库计算
No
ConcurrencyMode 指定属性是否执行并发性检查。要启用检查,设置值为Fixed。 No

复杂类型准备好后,你就可以使用它创建实体了。EntityType就是创建实体的地方。它有一个必须的Name特性,用来指定类的名称。还有两个可选特性:

  • Abstract——指定该类是否是抽象的。
  • BaseType——指定基类

Abstract和BaseType在使用继承中很重要。

在EntityType内部,Property元素作用于类中的标量(scalar)和复杂(complex)类型,Key元素指定哪个属性是实体的主键。如果属性是复杂类型(complex type),你通过Type特性设置它表示的复杂类型。Key元素没有特性;它只有一个PropertyRef节点。PropertyRef只有一个Name特性,包含属性的名称。

说了这么多,肯定还是不明白,下面列出代码,对照着很容易就明白了:

描述复杂类型和实体

你需要了解的概念模式(conceptual schema)就这些,但是这仅仅是EDM的第一部分。你还需要创建存储模式(storage schema)描述用来存储订单和它们详细信息的表。

在存储模式(storage schema)中描述数据库

存储说明文件包含数据库结构的详细信息。你可以在这里映射一些数据库对象:表,视图,存储过程和函数。在本章,只关注前两个对象;其他的在第十章讨论。

在OrderIT的EDMX文件中,存储模式在edmx:Edmx/edmx:Runtime/edmx:StorageModels路径下;但是如果你想手动创建,可以将它命名为OrderIT.ssdl并且在连接字符串中引用它。

在前边已经提到了,概念模式(conceptual schema)的元素可以在存储模式(storage schema)中重用。的确如此,但是一些节点在存储模式中有更多的特性以适应特定的数据库选项。还是从Schema节点开始吧。

SCHEMA

Schema是存储模式的根元素,它和概念模式的Schema差不多。它有两个与数据库通信所需的特性:

  • Provider——指定ADO.NET provider。
  • ProviderManifestToken——指定数据库的版本(例如:SQL Server使用2000,2005和2008)

下面的代码片段包含一个Schema元素的例子:

跟概念文件中一样,Schema有一个容器(container)节和详细(detail)节。

ENTITYCONTAINER

EntityContainer声明了稍后在文件中描述的数据库对象。它仅有的特性是Name,表示容器的名称。

设计器设置它的值为命名空间的名称加上StoreContainer。尽管你可以在概念模式里有多个EntityContainer,但是在存储模式中这是没有意义的,因为对象属于单个数据库。

在EntityContainer内,你为每个表或视图创建一个EntitySet元素。EntitySet在存储模式中比概念模式中有更多的特性:

  • Name——指定对象的逻辑名。设计器使用表的名称,但不是强制的。
  • EntityType——表示对象的完全限定名(FQN),格式为{namespace}.{name}。
  • Schema——表示表的持有者。
  • Table——表示表的物理名。Table不是强制的,当它忽略时就是用Name。
  • store:Type——指定对象是表(Tables)还是视图(Views)
定义数据库表和视图

下面描述表的结构。

ENTITYTYPE

使用EntityType元素描述对象,大部分和概念文件中一样。你必须为每一个EntityContainer中的EntitySet创建一个元素。在概念文件中,是继承结构部分的类只有一个EntitySet,但是有多个EntityType。数据库本身不支持继承,所以在存储文件中不可能有一个实体集和多个表或者视图。

每个EntityType节点有一个Name特性,它的值必须匹配EntityContainer中的EntitySet的Name特性。

在EntityType元素内,你必须包含描述数据库对象的元素。你再一次的使用Property声名列,Key和PropertyRef指定谁组成主键。

SSDL中的Property和CSDL中的属性差不多。SSDL增加了一些额外的特性以适应数据库需要:

  • Collation——指定列的排序规则。
  • DefaultValue——指定列的默认值
  • Unicode——指定列是否是Unicode。
描述Order和OrderDetail数据库表

存储文件准备好了,现在只缺少将概念模式和存储模式链接起来的映射模式了。

创建映射文件(Mapping file)

映射文件起到了连接数据库和类的桥梁作用。当模型中一个类对应一个表时,映射很简单;但是一旦事情变得复杂,映射也变得复杂。

OrderIT的EDMX文件中,edmx:Edmx/edmx:Runtime/edmx:Mapping是映射节(mapping section)的路径。如果你想手动创建该文件,可以创建OrderIT.msl并在连接字符串中引用它。

映射文件不描述实体或表,而是映射它们。因此,这个文件的结构完全不同于那两个。这也就意味着你必须学习一堆新的XML标签和特性,如下图所示:

最直接的不同就是根元素,这里是Mapping。

MAPPING AND ENTITYCONTAINERMAPPING

Mapping节点的声明是固定的,你不能加任何自定义的信息。它内部只允许是EntityContainerMapping。StorageEntityContainer特性标识存储模式容器(storage schema container),CdmEntityContainer标识概念模式容器(conceptual schema container)。

在映射(mapping)中定义容器(container)

在实体容器连接之后,下一步就是映射表和视图到类。这是通过在EntityContainerMapping元素中嵌入EntitySetMapping元素完成的。

ENTITYSETMAPPING, ENTITYTYPEMAPPING, AND MAPPINGFRAGMENT

映射实体到表需要四个步骤。通过定义实体集和其内部的实体开始。在实体内部,你指定实体映射到的表,最后指定column/property两者的关联。

EntitySetMapping是让你说明你将映射哪个实体的节点。唯一的特性是Name,用于指定实体集的名称。在EntitySetMapping内部,为每一个实体集嵌入一个EntityTypeMapping元素。实体的类型通过TypeName特性设置,按照IsTypeOf(EntityName)格式,EntityName是实体的完全限定名(FQN)。

现在开始映射表。因为一个类可以映射一个或多个表(Order映射到Order表,但是Shirt映射到Product和Shirt表),在EntityTypeMapping元素内,为每个涉及到表指定了一个MappingFragment元素。它只有一个StoreEntitySet特性,并且它必须匹配存储模式实体集的Name特性。

定义实体集,类和映射表

现在已经有了实体集,类,和表。可以开始属性到列的映射过程了。

SCALARPROPERTY AND COMPLEXPROPERTY

ScalarProperty元素映射类的标量属性(scalar property)到表或视图的列。它只有两个特性:

  • Name——表示属性的名称。
  • ColumnName——表示映射的列。

ComplexProperty用于映射复杂类型(complex type)。这个节点也有两个特性:

  • Name——指定属性的名称
  • TypeName——包含复杂类型的完全限定名(FQN)

ComplexProperty本身是没用的,必须将它的属性映射到列,需要嵌入ScalarProperty元素完成。

定义属性和列的映射

到目前为止,我们已经讨论了实体,并且忽略了它们之间的关联,但是是实体间的关联才是模型的真正本质。映射它们并不复杂,但是仍然要接触EDM的三个模式(schema)。幸运的是,你已经了解了模式(schema),这将简单很多。

下一篇将在模型中定义关系。

转载于:https://www.cnblogs.com/nianming/archive/2011/10/27/2226929.html

你可能感兴趣的文章
PHP 时区设置
查看>>
UVALive 5790 Ball Stacking 解题报告
查看>>
深入redis内部--事件处理机制
查看>>
[再寄小读者之数学篇](2014-05-23 $\ln x-ax=0$ 有两个根时的估计)
查看>>
(弃)解读Openstack Identity API v2.0
查看>>
Linux目录结构
查看>>
微信分享JS接口失效说明及解决方案
查看>>
ThinkPHP项目笔记之RBAC(权限)下篇
查看>>
log4j配置具体解释
查看>>
买低配车,更保值?!坊间有一句戏言:买车都要买低配
查看>>
数据迁移
查看>>
ubuntu14中创建python虚拟环境
查看>>
简单两步使用node发送qq邮件
查看>>
CSS
查看>>
区块链架构
查看>>
PHP Primary script unknown 终极解决方法
查看>>
3D文本悬停改变效果
查看>>
递归算法的时间复杂度
查看>>
有点不安全却又一亮的 Go unsafe.Pointer
查看>>
Linux安装mysql 8.0
查看>>