目录

1.前言

2.约定

2.1 主键约定

2.2 关系约定

2.3 复杂类型约定

3.数据注释

3.1 主键

3.2 必需

3.3 MaxLength和MinLength

3.4 NotMapped

3.5 复杂属性

3.6 ConcurrencyCheck

4.Fluent API

5.总结

前言  

  在之前的Code First示例中,我们编写好代码直接运行,就自动会自定创建数据库,并且根据定义好的类创建了对应的表,更加神奇的时候竟然还能自动创建主键、外键等等。这是如何实现的呢?如果我们想指定特定的属性为主键又该如何实现呢? 

约定  

  其实Code First生成模型的时候会通过约定(Conventions)来检测,约定实际上是EF定义的一组规则集,用于在使用Code First时根据这些规则生成模型,是在System.Data.Entity.ModelConfiguration.Convertions命名空间中定义的,我们可以去扩展约定,当然也可以不是使用内置的约定。EF内置约定如下:

  ORM系列之二:EF(4) 约定、注释、Fluent API-LMLPHP

  从上图我们可以看到定义了复杂属性预定、外键预定、主键约定,当然还有很多很多,EF也一直扩充内置约定。

  

  主键约定  

  如果类的属性名为“ID”(不区分大小写)或者类名的后面跟有“ID”,则Code First会推断该属性是主键。如果主键属性的类型为数值或者GUID,则将其配置为标识列。

  ORM系列之二:EF(4) 约定、注释、Fluent API-LMLPHP

  如上图所示,DepartmentID会被约定为主键。

  关系约定  

  如果类A中的属性中包含他类B的属性(该属性在类B本身中约定为主键),并且类A中包含类B,则Code First会自动推断出关联关系,并确定外键。

  ORM系列之二:EF(4) 约定、注释、Fluent API-LMLPHP

  如上图所示,在DepartmentID在Course表中会定义为外键,并且Department表和Course表会自动建立关联关系。

  

  复杂类型约定

  当Code First无法推断出主键,并没有通过其他方式设置主键,则该类型会被自动当做复杂属性。对于以下类定义,Code First推断Details是复杂属性,因为它没有主键。

  ORM系列之二:EF(4) 约定、注释、Fluent API-LMLPHP

  更多参考:https://msdn.microsoft.com/zh-cn/library/jj679962(v=vs.113).aspx

数据注释  

  默认情况下,Code First会根据约定定义模型,但是默认的约定规则毕竟有限,Code First同样允许通过数据注释来定义模型,而且如果没有移除约定的话,会先通过约定定义,再通过数据注释来定义,那么数据注释是如何实现的呢?

  主键  

  类中需要通过数据注释来定义主键,非常简单,只需要在对应的属性上面加上KEY关键字,如下所示:

  ORM系列之二:EF(4) 约定、注释、Fluent API-LMLPHP

  必需  

  属性上面加上Requied关键字,说明这个属性是必需的,即不允许为空。

  ORM系列之二:EF(4) 约定、注释、Fluent API-LMLPHP

  MaxLength和MinLength  

  使用MaxLength和MinLength特性,可以限定字段的范围。

  ORM系列之二:EF(4) 约定、注释、Fluent API-LMLPHP

  上图定义BloggerName长度为5-10。

  NotMapped  

  不映射,顾名思义就是不会将类的属性映射到数据库表的字段上,标记了NotMapped特性不会在数据库表中生成对应的字段。

  ORM系列之二:EF(4) 约定、注释、Fluent API-LMLPHP

  复杂属性  

  在类名上面增加ComplexType特性,并且其他类中某属性为该类时,该类映射到数据时就会变成复杂属性。

  ORM系列之二:EF(4) 约定、注释、Fluent API-LMLPHP

  

  Blog类中添加一个属性为BlogDetails。

public BlogDetails BlogDetail { get; set; }

    

  ConcurrencyCheck  

  ConcurrencyCheck注释可用于标记要在用户编辑或删除实体时用于在数据库中进行并发检查的一个或多个属性。

  ORM系列之二:EF(4) 约定、注释、Fluent API-LMLPHP  

  调用 SaveChanges 时,因为 BloggerName 字段上具有 ConcurrencyCheck 注释,所以在更新中将使用该属性的初始值。该命令将尝试通过同时依据键值和 BloggerName 的初始值进行筛选来查找正确的行。下面是发送到数据库的 UPDATE 命令的关键部分,在其中您可以看到该命令将更新 PrimaryTrackingKey 为 1 且 BloggerName 为“Julie”(这是从数据库中检索到该博客时的初始值)的行。

  ORM系列之二:EF(4) 约定、注释、Fluent API-LMLPHP

  如果在此期间有人更改了该博客的博主姓名,则此更新将失败,并引发 DbUpdateConcurrencyException 并且需要处理该异常。

  

  更多参考:https://msdn.microsoft.com/zh-cn/library/jj591583(v=vs.113).aspx

Fluent API  

  Code First模式下,默认行为是使用一组EF内嵌的约定规则将类映射到表。但有时候约定无法满足需求,这是我们还可以通过数据注释的方式来实现,但是数据注释功能仍然有限,好在EF提供了更加灵活的方式——Fluent API。

  Fluent API功能非常强大,它在通过重写DbContext上的OnModelCreating方法来实现自定义规则,可以设置主键、组合主键、外键、指定长度、指定属性是否映射到表等等。

  ORM系列之二:EF(4) 约定、注释、Fluent API-LMLPHP

  

  更多参考:https://msdn.microsoft.com/zh-cn/library/jj591617(v=vs.113).aspx

总结

  既然EF内置了很多约定规则,为何又可以通过数据注释方式来定义模型,而且还可以通过功能更加强大的Fluent API自定义模型。

  虽然内置的约定规则非常多,而且我想微软以后还会不断加入更多的约定,但是这些约定毕竟还是有限的,不能够满足所有用户需求,所以允许通过数据注释的方式来增加定义模型,但是有些情况通过数据注释依然是无法满足(例如:一个类型生成一张表还是多张表),这是我们可以通过Fluent API在生成模型之前自定义模型。

  综上所述,我们得知这三种定义模型的优先级为:约定—>数据注释—>Fluent API。

  

05-11 17:26