EntityFrameWork 课程2

来源:互联网 发布:家园2 mac 中文版 编辑:程序博客网 时间:2024/06/05 19:55

守则第一公约:

我们已经看到实体框架如何在上一节中创建来自域类的数据库表。在这里,我们将学习EF 6.x中默认的Code-First惯例。

什么是公约?

约定是默认规则集,当使用Code-First方法时,会自动配置基于域类的概念模型。这些EF 6.x Code-First约定在System.Data.Entity.ModelConfiguration.Conventions命名空间中定义

让我们来看看各种Code-First惯例的概述。

类型发现:

在前一节中,我们创建了一个上下文类,其中包含域类的DbSet <TEntity>类型属性。Code-First将在数据库中为上下文类中的所有DbSet属性创建表,如前所述。

EF还为上下文类中没有包含DbSet属性的域类创建数据库表,但它们作为其他域类中的引用属性包含在上下文类中的DbSet属性中。(即使引用的类型是在不同的程序集中定义的)

例如,以下学生类包括对教师类的引用。但是,上下文类不包括教师作为DbSet属性。

public class Student{    public int StudentID { get; set; }    public string StudentName { get; set; }    public DateTime DateOfBirth { get; set; }    public byte[]  Photo { get; set; }    public decimal Height { get; set; }    public float Weight { get; set; }            public Teacher Teacher { get; set; }    public Standard Standard { get; set; }}public class Teacher{    public int TeacherId { get; set; }    public string TeacherName { get; set; }}  
以下是不包含DbSet类型属性的上下文类。

namespace EF_Code_First_Tutorials{    public class SchoolContext: DbContext     {        public SchoolContext(): base()        {        }                    public DbSet<Student> Students { get; set; }        public DbSet<Standard> Standards { get; set; }    }}
因此,即使教师不是DbSet类型,EF也会将其包含在概念模型中,并为其创建数据库表,如下所示。


即使上下文类只包含作为DbSet属性的基类,Code-First也包括派生类。

类型发现的约定是:

  1. EF为上下文类中定义为DbSet属性的类型创建表。
  2. EF为包含在实体类型中的引用类型创建表,即使它们是在不同的程序集中定义的。
  3. 即使只将基类定义为DbSet属性,EF也会为派生类创建表。

主要关键公约:

在上一节中,我们已经看到EF自动在每个表中创建一个主键。默认情况下,如果属性名称是Id或<类名> Id(不区分大小写),则EF会为属性创建主键。主键属性的数据类型可以是任何东西,但是如果是数字或GUID,那么它将被配置为数据库中的标识列。

如果您已经定义了除Id或<ClassName> Id以外的关键属性,则将抛出ModelValidationException。例如,考虑以下标准类:

public class Student{    public int StudID { get; set; }    public string StudentName { get; set; }    public DateTime DateOfBirth { get; set; }    public byte[]  Photo { get; set; }    public decimal Height { get; set; }    public float Weight { get; set; }}

在上面的例子中,学生类是包含StudId属性而不是StudentIdId所以,EF会抛出以下异常,因为没有Key属性。

EntityFramework.dll中出现'System.Data.Entity.ModelConfiguration.ModelValidationException'EntityType'Student' 
没有定义键。定义这个EntityType的关键。

使用DataAnnotationsFluent APIStudId属性配置为主键。

注意:每个实体类必须在实体框架中包含关键属性。

关系公约:

EF 6默认使用导航属性来推断一对多关系。访问“一对多关系章节”以获取更多信息。

注意: EF 6不包括一对一和多对多关系的默认约定。您需要使用Fluent API或DataAnnotation进行配置。

外键公约:

我们已经在上面看到Code First在遇到导航属性时会自动插入一个外键。建议在关系的依赖端包含一个外键属性。考虑下面的例子:

public class Student{    public int StudentID { get; set; }    public string StudentName { get; set; }    public DateTime DateOfBirth { get; set; }    public byte[]  Photo { get; set; }    public decimal Height { get; set; }    public float Weight { get; set; }            //Foreign key for Standard    public int StandardId { get; set; }    public Standard Standard { get; set; }}public class Standard{    public int StandardId { get; set; }    public string StandardName { get; set; }        public IList<Student> Students { get; set; }}
正如你在上面的代码中看到的,Student类包括外键StandardId,它是Standard类中的关键属性。现在,Code First将在Students类中创建StandardId列,而不是Standard_StandardId列,如下所示。

请注意,上图中的StandardId外键不为空。这是因为int数据类型不可为空。

代码首先根据外键的可空性推断关系的多重性。如果该属性可以为空,则该关系被注册为空。否则,关系被注册为NOT NULL。在上面的Student类中将StandardId属性的数据类型修改为intto以Nullable<int>在Students表中创建一个可为空的外键列。

复杂类型约定:

Code First为不包含key属性的类创建复杂类型,而且主键未使用DataAnnotation或Fluent API注册。

默认代码优先约定:

下表列出了默认代码的第一个约定:

默认约定为描述表名<实体类名称> +' 
s'EF将创建数据库表,实体类名后缀为's'主键名称1)Id 
2)<Entity Class Name> +“Id”(不区分大小写) 

EF将为名为Id或<Entity Class Name> +“Id”的属性创建主键列(不区分大小写)外键属性名称默认情况下,EF将查找与主体实体主键名称相同的外键属性。 
如果外键属性不存在,则EF将使用<依赖的导航属性名称> +“_”+ <主体实体主键属性名称>在Db表中创建FK列, 
例如,如果学生实体,EF将创建Standard_StandardId外键列在标准包含标准标识的情况下不包含外键属性空列EF为所有引用类型属性和可空的原始属性创建空列。不是空列EF为PrimaryKey属性和非空值类型属性创建NotNull列。数据库列顺序EF将创建与实体类中的属性顺序相同的数据库列。但是,主键列会先移动。映射到数据库的属性默认情况下,所有属性将映射到数据库。使用[NotMapped]属性从数据库映射中排除属性或类。级联删除启用默认情况下,所有类型的关系。

下表列出了与SQL数据类型和主键列数据类型和长度映射的C#数据类型。

C#数据类型相关的DB列数据类型PK列数据类型和长度INTINTint,标识列增加1为nvarchar(最大)为nvarchar(128)十进制十进制(18,2)十进制(18,2)浮动真实真实字节[]VARBINARY(最大)VARBINARY(128)约会时间约会时间约会时间布尔字节TINYINTTINYINTSMALLINTSMALLINTBIGINTBIGINT浮动浮动烧焦没有映射没有映射为sbyte没有映射 
(抛出异常)没有映射目的没有映射没有映射

这是代码第一个约定的概述。这些约定可以使用DataAnnotation或Fluent API来重写。