Code First之所以能够让开发人员以一种更加高效、灵活的方式进行数据操作有一个重要的原因在于它的约定配置。现在软件开发越来复杂,大家也都试图将软件设计的越来越灵活,很多内容我们都希望是可配置的,但是过多的配置也会带来很大的工作量,解决这个问题的方法就是约定。对于一些简单的,不太可能经常变化的内容我们以一种约定的方式进行设计。使用过其他ORM框架的朋友可能知道一般ORM都有对应的映射配置文件(一般是一个Xml文件),但是EF并没有。在EF中是以一种约定的方式进行表、列同实体类进行映射的,与此同时为了提高最大的灵活性EF中可以通过Fluent API和Data Annotations两种方式对映射进行灵活配置。

EF默认约定

我们先来看一下EF对于数据类(概念模型,或域模型)的默认约定:

  • 将数据类的类名复数形式作为数据表名称,并且使用“dbo”作为默认架构。

例如定义一个Person数据类,那么将会自动生成“dbo.People”表。

  • 将数据类中的“ID”属性或者“<类名>+ID”作为主键(不区分大小写),并且如果该列为数值类型或者GUID列将作为标识列。

例如在Order类中如果有ID或者OrderID属性将默认作为主键,二者均出现优先使用 “ID”属性。

  • 使用导航属性约束两个表之间的关系,在从表数据类中除了导航属性,推荐定义一个外键属性在从表数据类中(如果不指定将默认生成一个“<主表类名>+<主表类的主键名>”的外键列;此外在主表中推荐定义一个集合从表属性用户导航,当然这个属性不定义也可以正常生成外键关系但是不利于使用),具体规则:“<导航属性名>+<主表的主键属性名>”或者“<主表类名>+<主键属性名>”又或者“<主表的主键属性名>”,其属性名不区分大小写并且如果出现多种匹配按照先后顺序匹配;如果不存在外键属性则外键关系注册为可选的,否则注册为必选项并且此时将设置级联删除关系;如果在从表类中有多个导航属性对应同一个数据类那么需要使用fluent API或者Data Annotations进行手动配置。

例如有一个Order类,主键为OrderID,在OrderDetail类中有一个导航属性Order(Order类型),那么当你定义一个OrderID在OrderDetail中,那么在Order和OrderDetail直接将建立一个级联删除关系。

  • 当EF按照上述规则在数据类中没有找到主键属性时(或者通过fluent API、Data Annotations没有定义)将认为此类为“复杂类型”(对于不了解复杂类型的朋友请点击这里What is a Complex Type)。

例如在“Person”数据类中有一个“Name”属性,但是数据库中可能将“Name”分为FirstName和LastName存储,此时就可以定义一个Name类,在此类中不定义主键列定义“FirstName”和“LastName”属性,就会在表“dbo.People”中生成“Name_FirstName”和“Name_LastName”列。

定义约定

EF的默认约定不是一成不变的,我们可以选择移除和修改它,例如EF默认生成数据表时将数据类名的复数形式作为表名,下面的代码就可以移除这个规则:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Data.Entity;
  6. using CodeFirst.Entities;
  7. using System.ComponentModel.DataAnnotations.Schema;
  8. using System.Data.Entity.ModelConfiguration.Conventions;
  9. namespace CodeFirst
  10. {
  11. public class OrderContext:DbContext
  12. {
  13. public OrderContext()
  14. : base("CodeFirstDb")
  15. {
  16. Database.SetInitializer<OrderContext>(
  17. new DropCreateDatabaseIfModelChanges<OrderContext>()
  18. );
  19. }
  20. public DbSet<Person> Person
  21. {
  22. get;
  23. set;
  24. }
  25. protected override void OnModelCreating(DbModelBuilder modelBuilder)
  26. {
  27. base.OnModelCreating(modelBuilder);
  28. modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
  29. }
  30. }
  31. }
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Entity;
using CodeFirst.Entities;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity.ModelConfiguration.Conventions; namespace CodeFirst
{
public class OrderContext:DbContext
{
public OrderContext()
: base("CodeFirstDb")
{
Database.SetInitializer<OrderContext>(
new DropCreateDatabaseIfModelChanges<OrderContext>()
);
} public DbSet<Person> Person
{
get;
set;
} protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
}
}

这些规则都在“System.Data.Entity.ModelConfiguration.Conventions”命名空间下,可以根据实际情况进行选择。

一般情况下我们是不需要移除默认约定的,我们更多的时候是要修改丰富这些约定,达到对生成规则的更多细节控制。在EF提供了两种方式进行映射配置:Data Annotations和Fluent API。

DataAnnotations

DataAnnotations是ASP.NET WEB中添加的一种验证方式,但是在EF中它又可以对映射关系进行控制,相比较Fluent API使用起来要简单一些。下面我们通过一个例子对DataAnnotations进行说明。在此我们假设有一个“Employee”类用于描述员工信息,一个“Customer”类用于描述客户信息,还有一个“Order”类用于描述订单信息,此外还有一个“Name”复杂类型表示人员姓名。在Order类中有一个属性“Customer”用于描述此订单的客户,它是“Customer”类型;还有一个“DeliverPerson”属性用于描述订单发货人,一个“CheckPerson”属性用户描述订单拣货人,它们都是“Employee”类型。下面是具体代码:

Employee类:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel.DataAnnotations;
  4. using System.ComponentModel.DataAnnotations.Schema;
  5. using System.Linq;
  6. using System.Text;
  7. namespace CodeFirst.Entities
  8. {
  9. [Table("People",Schema="Person")]
  10. public class Employee
  11. {
  12. [Key]
  13. public int No
  14. {
  15. get;
  16. set;
  17. }
  18. public Name Name
  19. {
  20. get;
  21. set;
  22. }
  23. [MinLength(5),MaxLength(30)]
  24. public string Title
  25. {
  26. get;
  27. set;
  28. }
  29. [Required]
  30. public DateTime BirthDate
  31. {
  32. get;
  33. set;
  34. }
  35. [ConcurrencyCheck]
  36. public string Address
  37. {
  38. get;
  39. set;
  40. }
  41. [Column("Notes",TypeName="ntext",Order=5)]
  42. public string Note
  43. {
  44. get;
  45. set;
  46. }
  47. [DatabaseGenerated(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Computed)]
  48. public DateTime CreateDate
  49. {
  50. get;
  51. set;
  52. }
  53. [NotMapped]
  54. public string PhotoPath
  55. {
  56. get;
  57. set;
  58. }
  59. [Timestamp]
  60. public byte[] TimeStamp
  61. {
  62. get;
  63. set;
  64. }
  65. [InverseProperty("DeliverPerson")]
  66. public List<Order> DeliverOrder
  67. {
  68. get;
  69. set;
  70. }
  71. [InverseProperty("CheckPerson")]
  72. public List<Order> CheckOrder
  73. {
  74. get;
  75. set;
  76. }
  77. }
  78. }
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Text; namespace CodeFirst.Entities
{
[Table("People",Schema="Person")]
public class Employee
{
[Key]
public int No
{
get;
set;
} public Name Name
{
get;
set;
} [MinLength(5),MaxLength(30)]
public string Title
{
get;
set;
} [Required]
public DateTime BirthDate
{
get;
set;
} [ConcurrencyCheck]
public string Address
{
get;
set;
} [Column("Notes",TypeName="ntext",Order=5)]
public string Note
{
get;
set;
} [DatabaseGenerated(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Computed)]
public DateTime CreateDate
{
get;
set;
} [NotMapped]
public string PhotoPath
{
get;
set;
} [Timestamp]
public byte[] TimeStamp
{
get;
set;
} [InverseProperty("DeliverPerson")]
public List<Order> DeliverOrder
{
get;
set;
} [InverseProperty("CheckPerson")]
public List<Order> CheckOrder
{
get;
set;
}
}
}

Customer类:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. namespace CodeFirst.Entities
  6. {
  7. public class Customer
  8. {
  9. public int CustomerID
  10. {
  11. get;
  12. set;
  13. }
  14. public string CompanyName
  15. {
  16. get;
  17. set;
  18. }
  19. }
  20. }
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text; namespace CodeFirst.Entities
{
public class Customer
{
public int CustomerID
{
get;
set;
} public string CompanyName
{
get;
set;
}
}
}

Name类:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel.DataAnnotations.Schema;
  4. using System.Linq;
  5. using System.Text;
  6. namespace CodeFirst.Entities
  7. {
  8. [ComplexType]//根据前面我们说的默认约定,不标记为ComplexType只有没有找到ID也会将Name作为一个复杂类型
  9. public class Name
  10. {
  11. public string FirstName
  12. {
  13. get;
  14. set;
  15. }
  16. public string LastName
  17. {
  18. get;
  19. set;
  20. }
  21. }
  22. }
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Text; namespace CodeFirst.Entities
{
[ComplexType]//根据前面我们说的默认约定,不标记为ComplexType只有没有找到ID也会将Name作为一个复杂类型
public class Name
{
public string FirstName
{
get;
set;
} public string LastName
{
get;
set;
}
}
}

Order类:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel.DataAnnotations.Schema;
  4. using System.Linq;
  5. using System.Text;
  6. namespace CodeFirst.Entities
  7. {
  8. public class Order
  9. {
  10. public int OrderID
  11. {
  12. get;
  13. set;
  14. }
  15. public string OrderTitle
  16. {
  17. get;
  18. set;
  19. }
  20. public string CustomerName
  21. {
  22. get;
  23. set;
  24. }
  25. public DateTime TransactionDate
  26. {
  27. get;
  28. set;
  29. }
  30. public int CustomerNo
  31. {
  32. get;
  33. set;
  34. }
  35. [ForeignKey("CustomerNo")]
  36. public Customer Customer
  37. {
  38. get;
  39. set;
  40. }
  41. public Employee DeliverPerson
  42. {
  43. get;
  44. set;
  45. }
  46. public Employee CheckPerson
  47. {
  48. get;
  49. set;
  50. }
  51. }
  52. }
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Text; namespace CodeFirst.Entities
{
public class Order
{
public int OrderID
{
get;
set;
} public string OrderTitle
{
get;
set;
} public string CustomerName
{
get;
set;
} public DateTime TransactionDate
{
get;
set;
} public int CustomerNo
{
get;
set;
} [ForeignKey("CustomerNo")]
public Customer Customer
{
get;
set;
} public Employee DeliverPerson
{
get;
set;
} public Employee CheckPerson
{
get;
set;
}
}
}

这是通过Data Annotations配置后EF生成的数据库表结构:

下面解释每个配置的作用

Table:用于指定生成表的表名、架构信息。

Column:用于指定生成数据表的列信息,如列名、数据类型、顺序等。

Key:用于指定任何名称的属性作为主键列并且默认将此列作为标识列(如果不想默认生成标识可以指定“DatabaseGenerated”属性的值为“None”),如果不指定此标记属性,将根据EF默认约定创建主键。如上代码指定“No”为“Employee”的主键。

Required:用户指定非空列,如上面的“BirthDay”创建列之后为“not null”列。

MinLengthMaxLength:指定字段长度(此属性通常可以用户客户端验证),例如上面“Title”定义成了“nvarchar(30)”。

ComplexType:用于标记复杂类型,对于包含复杂类型数据属性的类在生成数据表时复杂类型中每个属性都将作为其中一列。

DatabaseGenerated:用于指定数据库字段生成列,此类EF将不会直接更新。可以指定为计算列、标识列和非数据库生成列(例如给主键列指定此属性为“None”则不会生成标识列)。需要注意的是如果使用Code First字段生成数据库,那么此属性仅仅可以用于byte、timestamp列上,否则请应用在已经存在数据库的情况下,因为Code First无法判定生成具体计算列的公式(至少目前Code First还不支持公式配置)。

NotMapped:用户指定非映射列,标记此属性的列将不会在数据库中生成相应的列,例如上面的“PhotoPath ”没有在数据库中生成具体列,实际使用中它可能是通过其他具体规则得到的。

ConcurrencyCheck:用于进行并发检查,当一个用户A获得实体后通常会与数据库断开,此时如果另一个用户B也获得了实体并进行了修改,那么当A再进行更新时如果进行了“ConcurrencyCheck”标记则会进行并发检查,并根据原始值判断该实体是否存在,如果不存在则抛出异常。

TimeStamp:用于指定时间戳列,一个实体只能有一个TimeStamp列。在EF中TimeStamp是另一种并发控制方式,当EF遇到TimeStamp列会自动配置 “ConcurrencyCheck”及“DatabaseGenerated.Computed”来控制并发(通常我们推荐使用此方法)。

ForeignKey:用于指定外键列,我们知道按照上面提到的默认约定第三条,当我们在“Order”中定义了“Customer”属性后,如果定义“CustomerID” 属性(当然还有其他形式,大家可以按照声明说的默认约定3进行测试),那么EF会在“Order”表中创建一个“CustomerID”列并建立与“Customer”表的外键关系。但是如果像上面定义“CustomerNo”属性并且不指定“ForeignKey”标记的话将达不到我们的预期,EF将默认创建一个“Customer_CustomerID”列并创建与“Customer”的外键约束,同时创建一个“CustomerNo”列。当然解决的方式大家已经看到了那就是给导航属性“Customer”指定“ForegnKey”标记并且指定外键列为“CustomerNo”(注意虽然在“Customer”中不定义“Order的导航属性”生成的表中也并没用任何问题,但是我们推荐您定义相应的导航属性)。

InverseProperty:用于定义多重外键关系约束。我们在EF中通过导航属性定义主外键关系,但是当主表中有两个外键约束时可能仅仅通过添加相应的导航属性就无法完成了,例如上面“Order”中“DeliverPerson”和“CheckPerson”均为“Employee”类型,按我们的预期当然是在生成“Order”表时生成两个外键列并创建与“Employee”外键约束关系,但是如果没有在“Employee”类的“DeliverOrder”和“CheckOrder”属性上标记  “InverseProperty”属性EF是无法识别这种关系的(具体结果可以看下图),当然解决方式就是在对应导航属性中标记“InverseProperty”并指定对于的列名。

注意:DataAnnotations可以同时在同一个类后者属性上使用多个标记属性,上面的例子中对于每个类或属性只使用了一个单独的标记属性是为了说明起来更加简单;另外声明的例子中同时使用“ConcurrencyCheck”和“TimeStamp”指定了不同的列只是为了演示,一般情况下我们通过其中一种方式即可。

Fluent API

Fluent API一般配置

尽管我们可以通过Data Annotations进行映射关系约定,但是相比较而言Fluent API的功能更加强大,从功能上而言Data Annotations是Fluent API的一个子集, Data Annotations可以实现的功能Fluent API都能实现。下面我们先看一个例子,在这个例子中我们通过Fluent API约定方式实现上面Data Annotations的功能并且包含更多控制:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Data.Entity;
  6. using CodeFirst.Entities;
  7. using System.ComponentModel.DataAnnotations.Schema;
  8. using System.Data.Entity.ModelConfiguration.Conventions;
  9. namespace CodeFirst
  10. {
  11. public class OrderContext:DbContext
  12. {
  13. public OrderContext()
  14. : base("CodeFirstDb")
  15. {
  16. Database.SetInitializer<OrderContext>(
  17. new DropCreateDatabaseIfModelChanges<OrderContext>()
  18. );
  19. }
  20. public DbSet<Order> Orders
  21. {
  22. get;
  23. set;
  24. }
  25. public DbSet<Employee> Employees
  26. {
  27. get;
  28. set;
  29. }
  30. protected override void OnModelCreating(DbModelBuilder modelBuilder)
  31. {
  32. base.OnModelCreating(modelBuilder);
  33. modelBuilder.Entity<Employee>().ToTable("People", "Person");//指定“Employee”对应表名及架构
  34. modelBuilder.Entity<Employee>().HasKey(emp => emp.No);//定义主键为“No”
  35. //modelBuilder.Entity<Employee>().HasKey(emp => new { emp.No, emp.Title });//指定"No"和“Title”作为复合主键,使用Data Annotations无法做到
  36. modelBuilder.Entity<Employee>().Property(emp => emp.No).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);//去掉标识列,前面我们也提到过通过Data Annotations通用可以去掉主键默认标示属性
  37. modelBuilder.Entity<Employee>().Property(emp => emp.Title).HasMaxLength(30);//指定“Title”最大长度为30
  38. modelBuilder.Entity<Employee>().Property(emp => emp.BirthDate).IsRequired();//指定“BirthDate”为不可为空
  39. modelBuilder.Entity<Employee>().Property(emp => emp.Note).HasColumnName("Notes").HasColumnType("ntext");//指定“Note”对应列名为“Notes”,并指定数据类型
  40. modelBuilder.Entity<Employee>().Ignore(emp => emp.PhotoPath);//指定“PhotoPath”为非映射列
  41. modelBuilder.Entity<Employee>().Property(emp => emp.Title).IsUnicode(true);//“Title”列是否支持Unicode编码
  42. modelBuilder.ComplexType<Name>().Property(n=>n.FirstName).HasMaxLength(50);//指定Name为复杂数据类型,并指定复杂类型中“FirstName”长度
  43. //modelBuilder.Entity<Employee>().Property(emp => emp.Name.LastName).HasMaxLength(20);//还可以通过这种方式指定复杂类型“Name”的“LastName”列的长度
  44. //modelBuilder.Entity<Employee>().Property(emp => emp.Address).IsConcurrencyToken();//指定“Address”进行并发控制,通常这一列我们知道为“TimeStamp”列而不是“Addree”这里只是为了说明可以标记其他列
  45. modelBuilder.Entity<Employee>().Property(emp => emp.TimeStamp).IsRowVersion();//通过指定“TimeStamp”进行并发版本控制
  46. }
  47. }
  48. }
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Entity;
using CodeFirst.Entities;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity.ModelConfiguration.Conventions; namespace CodeFirst
{
public class OrderContext:DbContext
{
public OrderContext()
: base("CodeFirstDb")
{
Database.SetInitializer<OrderContext>(
new DropCreateDatabaseIfModelChanges<OrderContext>()
);
} public DbSet<Order> Orders
{
get;
set;
} public DbSet<Employee> Employees
{
get;
set;
} protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Employee>().ToTable("People", "Person");//指定“Employee”对应表名及架构 modelBuilder.Entity<Employee>().HasKey(emp => emp.No);//定义主键为“No”
//modelBuilder.Entity<Employee>().HasKey(emp => new { emp.No, emp.Title });//指定"No"和“Title”作为复合主键,使用Data Annotations无法做到 modelBuilder.Entity<Employee>().Property(emp => emp.No).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);//去掉标识列,前面我们也提到过通过Data Annotations通用可以去掉主键默认标示属性 modelBuilder.Entity<Employee>().Property(emp => emp.Title).HasMaxLength(30);//指定“Title”最大长度为30 modelBuilder.Entity<Employee>().Property(emp => emp.BirthDate).IsRequired();//指定“BirthDate”为不可为空 modelBuilder.Entity<Employee>().Property(emp => emp.Note).HasColumnName("Notes").HasColumnType("ntext");//指定“Note”对应列名为“Notes”,并指定数据类型 modelBuilder.Entity<Employee>().Ignore(emp => emp.PhotoPath);//指定“PhotoPath”为非映射列 modelBuilder.Entity<Employee>().Property(emp => emp.Title).IsUnicode(true);//“Title”列是否支持Unicode编码 modelBuilder.ComplexType<Name>().Property(n=>n.FirstName).HasMaxLength(50);//指定Name为复杂数据类型,并指定复杂类型中“FirstName”长度
//modelBuilder.Entity<Employee>().Property(emp => emp.Name.LastName).HasMaxLength(20);//还可以通过这种方式指定复杂类型“Name”的“LastName”列的长度 //modelBuilder.Entity<Employee>().Property(emp => emp.Address).IsConcurrencyToken();//指定“Address”进行并发控制,通常这一列我们知道为“TimeStamp”列而不是“Addree”这里只是为了说明可以标记其他列
modelBuilder.Entity<Employee>().Property(emp => emp.TimeStamp).IsRowVersion();//通过指定“TimeStamp”进行并发版本控制
}
}
}

从上面的代码中可以看到基本上在Data Annotations中实现的功能使用Fluent API都实现了,并且在上面的代码注释中我也提到了一些Data Annotations无法实现的功能,具体代码基本上都已经注释了在此也不再解释了。

Fluent API关系配置

下面让看一下EF中关系配置的实现,看一下Fluent API如何进行实体关系约束,这里假设每个公司员工“Employee”在企业内部都有一个通讯账户“MessagingAcount”,这二者之间是一对一的关系;同时添加一个产品类“Product”,它与“Order”的关系是多对多的关系,具体定义如下:

MessageAcount类:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel.DataAnnotations;
  4. using System.Linq;
  5. using System.Text;
  6. namespace CodeFirst.Entities
  7. {
  8. public class MessagingAccount
  9. {
  10. [Key()]
  11. public int EmployeeNo
  12. {
  13. get;
  14. set;
  15. }
  16. public Employee Employee
  17. {
  18. get;
  19. set;
  20. }
  21. public string UserName
  22. {
  23. get;
  24. set;
  25. }
  26. public string Password
  27. {
  28. get;
  29. set;
  30. }
  31. }
  32. }
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text; namespace CodeFirst.Entities
{
public class MessagingAccount
{
[Key()]
public int EmployeeNo
{
get;
set;
} public Employee Employee
{
get;
set;
} public string UserName
{
get;
set;
} public string Password
{
get;
set;
} }
}

Product类:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. namespace CodeFirst.Entities
  6. {
  7. public class Product
  8. {
  9. public int ProductID
  10. {
  11. get;
  12. set;
  13. }
  14. public string ProductName
  15. {
  16. get;
  17. set;
  18. }
  19. public double UnitPrice
  20. {
  21. get;
  22. set;
  23. }
  24. public int OrderID
  25. {
  26. get;
  27. set;
  28. }
  29. public List<Order> Orders
  30. {
  31. get;
  32. set;
  33. }
  34. }
  35. }
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text; namespace CodeFirst.Entities
{
public class Product
{
public int ProductID
{
get;
set;
} public string ProductName
{
get;
set;
} public double UnitPrice
{
get;
set;
} public int OrderID
{
get;
set;
} public List<Order> Orders
{
get;
set;
} }
}

Employee类(添加了对应的属性):

  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel.DataAnnotations;
  4. using System.ComponentModel.DataAnnotations.Schema;
  5. using System.Linq;
  6. using System.Text;
  7. namespace CodeFirst.Entities
  8. {
  9. public class Employee
  10. {
  11. public int No
  12. {
  13. get;
  14. set;
  15. }
  16. public Name Name
  17. {
  18. get;
  19. set;
  20. }
  21. public string Title
  22. {
  23. get;
  24. set;
  25. }
  26. public DateTime BirthDate
  27. {
  28. get;
  29. set;
  30. }
  31. public string Address
  32. {
  33. get;
  34. set;
  35. }
  36. public string Note
  37. {
  38. get;
  39. set;
  40. }
  41. public DateTime CreateDate
  42. {
  43. get;
  44. set;
  45. }
  46. public string PhotoPath
  47. {
  48. get;
  49. set;
  50. }
  51. public byte[] TimeStamp
  52. {
  53. get;
  54. set;
  55. }
  56. public List<Order> DeliverOrder
  57. {
  58. get;
  59. set;
  60. }
  61. public List<Order> CheckOrder
  62. {
  63. get;
  64. set;
  65. }
  66. public MessagingAccount Acount
  67. {
  68. get;
  69. set;
  70. }
  71. }
  72. }
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Text; namespace CodeFirst.Entities
{
public class Employee
{
public int No
{
get;
set;
} public Name Name
{
get;
set;
} public string Title
{
get;
set;
} public DateTime BirthDate
{
get;
set;
} public string Address
{
get;
set;
} public string Note
{
get;
set;
} public DateTime CreateDate
{
get;
set;
} public string PhotoPath
{
get;
set;
} public byte[] TimeStamp
{
get;
set;
} public List<Order> DeliverOrder
{
get;
set;
} public List<Order> CheckOrder
{
get;
set;
} public MessagingAccount Acount
{
get;
set;
}
}
}

Order类(添加了对应的属性):

  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel.DataAnnotations.Schema;
  4. using System.Linq;
  5. using System.Text;
  6. namespace CodeFirst.Entities
  7. {
  8. public class Order
  9. {
  10. public int OrderID
  11. {
  12. get;
  13. set;
  14. }
  15. public string OrderTitle
  16. {
  17. get;
  18. set;
  19. }
  20. public string CustomerName
  21. {
  22. get;
  23. set;
  24. }
  25. public DateTime TransactionDate
  26. {
  27. get;
  28. set;
  29. }
  30. public int CustomerNo
  31. {
  32. get;
  33. set;
  34. }
  35. public Customer Customer
  36. {
  37. get;
  38. set;
  39. }
  40. public int ProductID
  41. {
  42. get;
  43. set;
  44. }
  45. public List<Product> Products
  46. {
  47. get;
  48. set;
  49. }
  50. public Employee DeliverPerson
  51. {
  52. get;
  53. set;
  54. }
  55. public Employee CheckPerson
  56. {
  57. get;
  58. set;
  59. }
  60. }
  61. }
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Text; namespace CodeFirst.Entities
{
public class Order
{
public int OrderID
{
get;
set;
} public string OrderTitle
{
get;
set;
} public string CustomerName
{
get;
set;
} public DateTime TransactionDate
{
get;
set;
} public int CustomerNo
{
get;
set;
} public Customer Customer
{
get;
set;
} public int ProductID
{
get;
set;
} public List<Product> Products
{
get;
set;
} public Employee DeliverPerson
{
get;
set;
} public Employee CheckPerson
{
get;
set;
}
}
}

OrderContext类,定义了数据类之间的关系,主要是关系配置部分:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Data.Entity;
  6. using CodeFirst.Entities;
  7. using System.ComponentModel.DataAnnotations.Schema;
  8. using System.Data.Entity.ModelConfiguration.Conventions;
  9. namespace CodeFirst
  10. {
  11. public class OrderContext:DbContext
  12. {
  13. public OrderContext()
  14. : base("CodeFirstDb")
  15. {
  16. Database.SetInitializer<OrderContext>(
  17. new DropCreateDatabaseIfModelChanges<OrderContext>()
  18. );
  19. }
  20. public DbSet<Order> Orders
  21. {
  22. get;
  23. set;
  24. }
  25. public DbSet<Employee> Employees
  26. {
  27. get;
  28. set;
  29. }
  30. protected override void OnModelCreating(DbModelBuilder modelBuilder)
  31. {
  32. base.OnModelCreating(modelBuilder);
  33. modelBuilder.Entity<Employee>().ToTable("People", "Person");//指定“Employee”对应表名及架构
  34. modelBuilder.Entity<Employee>().HasKey(emp => emp.No);//定义主键为“No”
  35. //modelBuilder.Entity<Employee>().HasKey(emp => new { emp.No, emp.Title });//指定"No"和“Title”作为复合主键,使用Data Annotations无法做到
  36. modelBuilder.Entity<Employee>().Property(emp => emp.No).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);//去掉标识列,前面我们也提到过通过Data Annotations通用可以去掉主键默认标示属性
  37. modelBuilder.Entity<Employee>().Property(emp => emp.Title).HasMaxLength(30);//指定“Title”最大长度为30
  38. modelBuilder.Entity<Employee>().Property(emp => emp.BirthDate).IsRequired();//指定“BirthDate”为不可为空
  39. modelBuilder.Entity<Employee>().Property(emp => emp.Note).HasColumnName("Notes").HasColumnType("ntext");//指定“Note”对应列名为“Notes”,并指定数据类型
  40. modelBuilder.Entity<Employee>().Ignore(emp => emp.PhotoPath);//指定“PhotoPath”为非映射列
  41. //modelBuilder.Entity<Customer>().HasRequired(c=>c.Orders).WithMany().Map(m=>m.MapKey("CustomerOrder"));
  42. modelBuilder.Entity<Employee>().Property(emp => emp.Title).IsUnicode(true);//“Title”列是否支持Unicode编码
  43. modelBuilder.ComplexType<Name>().Property(n=>n.FirstName).HasMaxLength(50);//指定Name为复杂数据类型,并指定复杂类型中“FirstName”长度
  44. //modelBuilder.Entity<Employee>().Property(emp => emp.Name.LastName).HasMaxLength(20);//还可以通过这种方式指定复杂类型“Name”的“LastName”列的长度
  45. //modelBuilder.Entity<Employee>().Property(emp => emp.Address).IsConcurrencyToken();//指定“Address”进行并发控制,通常这一列我们知道为“TimeStamp”列而不是“Addree”这里只是为了说明可以标记其他列
  46. modelBuilder.Entity<Employee>().Property(emp => emp.TimeStamp).IsRowVersion();//通过指定“TimeStamp”进行并发版本控制
  47. /*下面代码演示EF中的关系约束*/
  48. //modelBuilder.Entity<MessagingAccount>().HasRequired(a => a.Employee).WithOptional(emp => emp.Acount);//配置一对零关系,允许存在一个Employee而不存在MessagingAcount的情况(注意在Employee中添加Acont属性)
  49. modelBuilder.Entity<Employee>().HasRequired(emp => emp.Acount).WithRequiredPrincipal(a => a.Employee);//配置一对一关系,和上面的WithOptionnal关系区别是每个Employee必须有一个MessagingAcount而每个MessageAcount也必须有一个Employee;但是Employee是主表,此时允许Employee单独持久化而不允许MessagingAcount单独持久化
  50. //注意配置一对一关系也可以使用WithRequiredDependent,只不过主表发生了变化,上面的语句与下面的语句是等价的
  51. //modelBuilder.Entity<MessagingAccount>().HasRequired(a => a.Employee).WithRequiredDependent(a => a.Acount);//
  52. //下面的方法解决了一对一的关系,此时Employee和MessageAcount将必须同时存在
  53. //modelBuilder.Entity<Employee>().HasRequired(emp => emp.Acount).WithMany().HasForeignKey(emp => emp.MessagingAccountID);
  54. //modelBuilder.Entity<MessagingAccount>().HasRequired(a => a.Employee).WithMany().HasForeignKey(a => a.EmployeeNo);
  55. //modelBuilder.Entity<Order>().HasRequired(o=>o.Customer).WithMany();//一对多的关系,一个Customer有多个Order(注意,运行之前先把Order中CustomerNo和Customer中的Orders属性删除掉,否则将生成两个外键一个是自动生成的另一个是Fluent API配置生成的,对应这种关系推荐使用默认生成)
  56. //modelBuilder.Entity<Order>().HasRequired(o => o.Customer).WithMany().WillCascadeOnDelete();//添加添加级联删除
  57. //modelBuilder.Entity<Order>().HasRequired(o => o.Customer).WithMany().Map(m=>m.MapKey("Customer_Order");//外键重命名
  58. //modelBuilder.Entity<Order>().HasRequired(o => o.Customer).WithMany().HasForeignKey(o => new { o.CustomerNo,o.CustomerName});//组合外键,注意本例中没有组合外键(CustomerName不是外键),这里只是举例而已
  59. //modelBuilder.Entity<Order>().HasRequired(o => o.Customer).WithMany(c=>c.Orders).HasForeignKey(o => o.CustomerNo);//指定外键(一般用户外键名不符合默认约束命名时)
  60. modelBuilder.Entity<Order>().HasMany(o => o.Products).WithMany(p => p.Orders).Map(m => {
  61. m.ToTable("OrderDetails");
  62. m.MapLeftKey("OrderID");
  63. m.MapRightKey("ProductID");
  64. });//配置多对多的关系,并指定了表名、对应的外键;注意如果不使用FluentAPI配置,Product和Order配置了相应的导航属性,EF也会默认生成一张表(表名为“<数据类1>+<数据类2>”)
  65. }
  66. }
  67. }
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Entity;
using CodeFirst.Entities;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity.ModelConfiguration.Conventions; namespace CodeFirst
{
public class OrderContext:DbContext
{
public OrderContext()
: base("CodeFirstDb")
{
Database.SetInitializer<OrderContext>(
new DropCreateDatabaseIfModelChanges<OrderContext>()
);
} public DbSet<Order> Orders
{
get;
set;
} public DbSet<Employee> Employees
{
get;
set;
} protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Employee>().ToTable("People", "Person");//指定“Employee”对应表名及架构 modelBuilder.Entity<Employee>().HasKey(emp => emp.No);//定义主键为“No”
//modelBuilder.Entity<Employee>().HasKey(emp => new { emp.No, emp.Title });//指定"No"和“Title”作为复合主键,使用Data Annotations无法做到 modelBuilder.Entity<Employee>().Property(emp => emp.No).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);//去掉标识列,前面我们也提到过通过Data Annotations通用可以去掉主键默认标示属性 modelBuilder.Entity<Employee>().Property(emp => emp.Title).HasMaxLength(30);//指定“Title”最大长度为30 modelBuilder.Entity<Employee>().Property(emp => emp.BirthDate).IsRequired();//指定“BirthDate”为不可为空 modelBuilder.Entity<Employee>().Property(emp => emp.Note).HasColumnName("Notes").HasColumnType("ntext");//指定“Note”对应列名为“Notes”,并指定数据类型 modelBuilder.Entity<Employee>().Ignore(emp => emp.PhotoPath);//指定“PhotoPath”为非映射列 //modelBuilder.Entity<Customer>().HasRequired(c=>c.Orders).WithMany().Map(m=>m.MapKey("CustomerOrder")); modelBuilder.Entity<Employee>().Property(emp => emp.Title).IsUnicode(true);//“Title”列是否支持Unicode编码 modelBuilder.ComplexType<Name>().Property(n=>n.FirstName).HasMaxLength(50);//指定Name为复杂数据类型,并指定复杂类型中“FirstName”长度
//modelBuilder.Entity<Employee>().Property(emp => emp.Name.LastName).HasMaxLength(20);//还可以通过这种方式指定复杂类型“Name”的“LastName”列的长度 //modelBuilder.Entity<Employee>().Property(emp => emp.Address).IsConcurrencyToken();//指定“Address”进行并发控制,通常这一列我们知道为“TimeStamp”列而不是“Addree”这里只是为了说明可以标记其他列
modelBuilder.Entity<Employee>().Property(emp => emp.TimeStamp).IsRowVersion();//通过指定“TimeStamp”进行并发版本控制 /*下面代码演示EF中的关系约束*/
//modelBuilder.Entity<MessagingAccount>().HasRequired(a => a.Employee).WithOptional(emp => emp.Acount);//配置一对零关系,允许存在一个Employee而不存在MessagingAcount的情况(注意在Employee中添加Acont属性)
modelBuilder.Entity<Employee>().HasRequired(emp => emp.Acount).WithRequiredPrincipal(a => a.Employee);//配置一对一关系,和上面的WithOptionnal关系区别是每个Employee必须有一个MessagingAcount而每个MessageAcount也必须有一个Employee;但是Employee是主表,此时允许Employee单独持久化而不允许MessagingAcount单独持久化
//注意配置一对一关系也可以使用WithRequiredDependent,只不过主表发生了变化,上面的语句与下面的语句是等价的
//modelBuilder.Entity<MessagingAccount>().HasRequired(a => a.Employee).WithRequiredDependent(a => a.Acount);//
//下面的方法解决了一对一的关系,此时Employee和MessageAcount将必须同时存在
//modelBuilder.Entity<Employee>().HasRequired(emp => emp.Acount).WithMany().HasForeignKey(emp => emp.MessagingAccountID);
//modelBuilder.Entity<MessagingAccount>().HasRequired(a => a.Employee).WithMany().HasForeignKey(a => a.EmployeeNo); //modelBuilder.Entity<Order>().HasRequired(o=>o.Customer).WithMany();//一对多的关系,一个Customer有多个Order(注意,运行之前先把Order中CustomerNo和Customer中的Orders属性删除掉,否则将生成两个外键一个是自动生成的另一个是Fluent API配置生成的,对应这种关系推荐使用默认生成)
//modelBuilder.Entity<Order>().HasRequired(o => o.Customer).WithMany().WillCascadeOnDelete();//添加添加级联删除
//modelBuilder.Entity<Order>().HasRequired(o => o.Customer).WithMany().Map(m=>m.MapKey("Customer_Order");//外键重命名
//modelBuilder.Entity<Order>().HasRequired(o => o.Customer).WithMany().HasForeignKey(o => new { o.CustomerNo,o.CustomerName});//组合外键,注意本例中没有组合外键(CustomerName不是外键),这里只是举例而已
//modelBuilder.Entity<Order>().HasRequired(o => o.Customer).WithMany(c=>c.Orders).HasForeignKey(o => o.CustomerNo);//指定外键(一般用户外键名不符合默认约束命名时) modelBuilder.Entity<Order>().HasMany(o => o.Products).WithMany(p => p.Orders).Map(m => {
m.ToTable("OrderDetails");
m.MapLeftKey("OrderID");
m.MapRightKey("ProductID");
});//配置多对多的关系,并指定了表名、对应的外键;注意如果不使用FluentAPI配置,Product和Order配置了相应的导航属性,EF也会默认生成一张表(表名为“<数据类1>+<数据类2>”)
}
}
}

运行生成的数据库结构如下图:

在上面的代码中我们着重看关系配置部分,我们注释了一部分关系约束配置代码主要是因为有些关系不能共存大家可以自己去掉执行试试,这部分代码希望初学者不要略过。关于上面的代码相信大家看注释都可以明白,这里我主要强调一点,那就是多重外键约束。大家通过上图已经看到CheckPerson和DeliverPerson的约束像在Data Annotations中提到的一样并没有达到我们的预期,其主要原因是因为EF并没有明白这种约束关系,解决办法很简单,只要配置“Employee”和“Order”一对多约束关系即可(注意只有配置一个属性即可,例如我们配置CheckPerson):

modelBuilder.Entity<Order>().HasRequired(o => o.CheckPerson).WithMany(emp => emp.CheckOrder).WillCascadeOnDelete();

Fluent API继承实现

下面看一下EF强大的继承实现,在EF中支持三种类型的继承实现:

  • Table-Per-Hierarchy(TPH):EF默认支持的类型,无需配置即可实现,整个层次在一张表中,基类中没有的属性被标记为可空,通过添加一个额外的“Discniminator”列进行类型区分;
  • Table-Per-Type(TPT):每个类型一张表,在子类中包含自身属性和一个指向基类的外键列;
  • Table-Per-Concrete Calss(TPC):每个类型一张表,但是和TPT不同的是子类中并没有创建外键列而是直接将基类的属性在子类中展开(子类表包含基类表的所有列);

在演示上面几种方式之前先让我们定义两个类“Worker”表示当前在职员工和“Retired”表示退休员工,它们继承于“Employee”。

TPH方式

TPH方式是EF默认支持,我们无需更多的配置即可完成。

Worker类:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. namespace CodeFirst.Entities
  6. {
  7. public class Worker:Employee
  8. {
  9. public decimal AnnualSalary
  10. {
  11. get;
  12. set;
  13. }
  14. }
  15. }
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text; namespace CodeFirst.Entities
{
public class Worker:Employee
{
public decimal AnnualSalary
{
get;
set;
}
}
}

Retired类:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. namespace CodeFirst.Entities
  6. {
  7. public class Retired:Employee
  8. {
  9. public decimal MonthlyPension
  10. {
  11. get;
  12. set;
  13. }
  14. }
  15. }
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text; namespace CodeFirst.Entities
{
public class Retired:Employee
{
public decimal MonthlyPension
{
get;
set;
}
}
}

接下来插入两条数据进行测试:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Data.Entity;
  4. using System.Data.SqlClient;
  5. using System.Linq;
  6. using System.Text;
  7. using CodeFirst;
  8. using CodeFirst.Entities;
  9. namespace CodeFirst
  10. {
  11. class Program
  12. {
  13. static void Main(string[] args)
  14. {
  15. using (var db = new OrderContext())
  16. {
  17. db.Workers.Add(new Worker()
  18. {
  19. No = 1,
  20. Name = new Name { FirstName="Stephen",LastName="Chow"},
  21. Title = "Software Architect",
  22. BirthDate=new DateTime(1976,10,10),
  23. Address="Peking",
  24. Note="",
  25. CreateDate=DateTime.Now,
  26. AnnualSalary=999999999
  27. });
  28. db.Retireds.Add(new Retired
  29. {
  30. No = 2,
  31. Name = new Name { FirstName = "Jeffrey", LastName = "Lee" },
  32. Title = "Software Development Engineer",
  33. BirthDate = new DateTime(1956, 8, 8),
  34. Address = "Hong Kong",
  35. Note = "",
  36. CreateDate = DateTime.Now,
  37. MonthlyPension=9999999
  38. });
  39. db.SaveChanges();
  40. }
  41. }
  42. }
  43. }
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using CodeFirst;
using CodeFirst.Entities; namespace CodeFirst
{
class Program
{
static void Main(string[] args)
{
using (var db = new OrderContext())
{
db.Workers.Add(new Worker()
{
No = 1,
Name = new Name { FirstName="Stephen",LastName="Chow"},
Title = "Software Architect",
BirthDate=new DateTime(1976,10,10),
Address="Peking",
Note="",
CreateDate=DateTime.Now,
AnnualSalary=999999999
}); db.Retireds.Add(new Retired
{
No = 2,
Name = new Name { FirstName = "Jeffrey", LastName = "Lee" },
Title = "Software Development Engineer",
BirthDate = new DateTime(1956, 8, 8),
Address = "Hong Kong",
Note = "",
CreateDate = DateTime.Now,
MonthlyPension=9999999
});
db.SaveChanges();
}
}
}
}

下面是具体结果:

TPT方式

使用TPT方式其实也十分简单,只需要配置基类及子类生成的表信息即可:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Data.Entity;
  6. using CodeFirst.Entities;
  7. using System.ComponentModel.DataAnnotations.Schema;
  8. using System.Data.Entity.ModelConfiguration.Conventions;
  9. namespace CodeFirst
  10. {
  11. public class OrderContext:DbContext
  12. {
  13. public OrderContext()
  14. : base("CodeFirstDb")
  15. {
  16. Database.SetInitializer<OrderContext>(
  17. new DropCreateDatabaseIfModelChanges<OrderContext>()
  18. );
  19. }
  20. public DbSet<Order> Orders
  21. {
  22. get;
  23. set;
  24. }
  25. public DbSet<Employee> Employees
  26. {
  27. get;
  28. set;
  29. }
  30. public DbSet<Worker> Workers
  31. {
  32. get;
  33. set;
  34. }
  35. public DbSet<Retired> Retireds
  36. {
  37. get;
  38. set;
  39. }
  40. protected override void OnModelCreating(DbModelBuilder modelBuilder)
  41. {
  42. base.OnModelCreating(modelBuilder);
  43. //modelBuilder.Entity<Employee>().ToTable("People", "Person");//指定“Employee”对应表名及架构
  44. modelBuilder.Entity<Employee>().HasKey(emp => emp.No);//定义主键为“No”
  45. //modelBuilder.Entity<Employee>().HasKey(emp => new { emp.No, emp.Title });//指定"No"和“Title”作为复合主键,使用Data Annotations无法做到
  46. modelBuilder.Entity<Employee>().Property(emp => emp.No).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);//去掉标识列,前面我们也提到过通过Data Annotations通用可以去掉主键默认标示属性
  47. modelBuilder.Entity<Employee>().Property(emp => emp.Title).HasMaxLength(30);//指定“Title”最大长度为30
  48. modelBuilder.Entity<Employee>().Property(emp => emp.BirthDate).IsRequired();//指定“BirthDate”为不可为空
  49. modelBuilder.Entity<Employee>().Property(emp => emp.Note).HasColumnName("Notes").HasColumnType("ntext");//指定“Note”对应列名为“Notes”,并指定数据类型
  50. modelBuilder.Entity<Employee>().Ignore(emp => emp.PhotoPath);//指定“PhotoPath”为非映射列
  51. //modelBuilder.Entity<Customer>().HasRequired(c=>c.Orders).WithMany().Map(m=>m.MapKey("CustomerOrder"));
  52. modelBuilder.Entity<Employee>().Property(emp => emp.Title).IsUnicode(true);//“Title”列是否支持Unicode编码
  53. modelBuilder.ComplexType<Name>().Property(n=>n.FirstName).HasMaxLength(50);//指定Name为复杂数据类型,并指定复杂类型中“FirstName”长度
  54. //modelBuilder.Entity<Employee>().Property(emp => emp.Name.LastName).HasMaxLength(20);//还可以通过这种方式指定复杂类型“Name”的“LastName”列的长度
  55. //modelBuilder.Entity<Employee>().Property(emp => emp.Address).IsConcurrencyToken();//指定“Address”进行并发控制,通常这一列我们知道为“TimeStamp”列而不是“Addree”这里只是为了说明可以标记其他列
  56. modelBuilder.Entity<Employee>().Property(emp => emp.TimeStamp).IsRowVersion();//通过指定“TimeStamp”进行并发版本控制
  57. /*下面代码演示EF中的继承关系实现*/
  58. //TPH默认支持,无需手动进行配置
  59. //TPT,只需要指定生成的表即可
  60. modelBuilder.Entity<Employee>().ToTable("People", "Person");
  61. modelBuilder.Entity<Worker>().ToTable("Worker", "Person");
  62. modelBuilder.Entity<Retired>().ToTable("Retired", "Person");
  63. }
  64. }
  65. }
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Entity;
using CodeFirst.Entities;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity.ModelConfiguration.Conventions; namespace CodeFirst
{
public class OrderContext:DbContext
{
public OrderContext()
: base("CodeFirstDb")
{
Database.SetInitializer<OrderContext>(
new DropCreateDatabaseIfModelChanges<OrderContext>()
);
} public DbSet<Order> Orders
{
get;
set;
} public DbSet<Employee> Employees
{
get;
set;
} public DbSet<Worker> Workers
{
get;
set;
} public DbSet<Retired> Retireds
{
get;
set;
} protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
//modelBuilder.Entity<Employee>().ToTable("People", "Person");//指定“Employee”对应表名及架构 modelBuilder.Entity<Employee>().HasKey(emp => emp.No);//定义主键为“No”
//modelBuilder.Entity<Employee>().HasKey(emp => new { emp.No, emp.Title });//指定"No"和“Title”作为复合主键,使用Data Annotations无法做到 modelBuilder.Entity<Employee>().Property(emp => emp.No).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);//去掉标识列,前面我们也提到过通过Data Annotations通用可以去掉主键默认标示属性 modelBuilder.Entity<Employee>().Property(emp => emp.Title).HasMaxLength(30);//指定“Title”最大长度为30 modelBuilder.Entity<Employee>().Property(emp => emp.BirthDate).IsRequired();//指定“BirthDate”为不可为空 modelBuilder.Entity<Employee>().Property(emp => emp.Note).HasColumnName("Notes").HasColumnType("ntext");//指定“Note”对应列名为“Notes”,并指定数据类型 modelBuilder.Entity<Employee>().Ignore(emp => emp.PhotoPath);//指定“PhotoPath”为非映射列 //modelBuilder.Entity<Customer>().HasRequired(c=>c.Orders).WithMany().Map(m=>m.MapKey("CustomerOrder")); modelBuilder.Entity<Employee>().Property(emp => emp.Title).IsUnicode(true);//“Title”列是否支持Unicode编码 modelBuilder.ComplexType<Name>().Property(n=>n.FirstName).HasMaxLength(50);//指定Name为复杂数据类型,并指定复杂类型中“FirstName”长度
//modelBuilder.Entity<Employee>().Property(emp => emp.Name.LastName).HasMaxLength(20);//还可以通过这种方式指定复杂类型“Name”的“LastName”列的长度 //modelBuilder.Entity<Employee>().Property(emp => emp.Address).IsConcurrencyToken();//指定“Address”进行并发控制,通常这一列我们知道为“TimeStamp”列而不是“Addree”这里只是为了说明可以标记其他列
modelBuilder.Entity<Employee>().Property(emp => emp.TimeStamp).IsRowVersion();//通过指定“TimeStamp”进行并发版本控制 /*下面代码演示EF中的继承关系实现*/
//TPH默认支持,无需手动进行配置
//TPT,只需要指定生成的表即可
modelBuilder.Entity<Employee>().ToTable("People", "Person");
modelBuilder.Entity<Worker>().ToTable("Worker", "Person");
modelBuilder.Entity<Retired>().ToTable("Retired", "Person");
}
}
}

生成的表结构如下:

TPC方式

最后看一下TPC方式,TPC方式同TPT一样同样是每个类型创建一张表,不同的是TPC每个子类中只有子类特有属性和外键列,子类通过外键查找基类属性;而在TPC方式中每个子类和基类之间并没有创建约束关系,子类表中拥有自身属性和基类所有属性。TPC定义方式也很简单,只需要在子类中通过“MapInheritedProperties”方法指定集成基类属性即可:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Data.Entity;
  6. using CodeFirst.Entities;
  7. using System.ComponentModel.DataAnnotations.Schema;
  8. using System.Data.Entity.ModelConfiguration.Conventions;
  9. namespace CodeFirst
  10. {
  11. public class OrderContext:DbContext
  12. {
  13. public OrderContext()
  14. : base("CodeFirstDb")
  15. {
  16. Database.SetInitializer<OrderContext>(
  17. new DropCreateDatabaseIfModelChanges<OrderContext>()
  18. );
  19. }
  20. public DbSet<Order> Orders
  21. {
  22. get;
  23. set;
  24. }
  25. public DbSet<Employee> Employees
  26. {
  27. get;
  28. set;
  29. }
  30. public DbSet<Worker> Workers
  31. {
  32. get;
  33. set;
  34. }
  35. public DbSet<Retired> Retireds
  36. {
  37. get;
  38. set;
  39. }
  40. protected override void OnModelCreating(DbModelBuilder modelBuilder)
  41. {
  42. base.OnModelCreating(modelBuilder);
  43. //modelBuilder.Entity<Employee>().ToTable("People", "Person");//指定“Employee”对应表名及架构
  44. modelBuilder.Entity<Employee>().HasKey(emp => emp.No);//定义主键为“No”
  45. //modelBuilder.Entity<Employee>().HasKey(emp => new { emp.No, emp.Title });//指定"No"和“Title”作为复合主键,使用Data Annotations无法做到
  46. modelBuilder.Entity<Employee>().Property(emp => emp.No).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);//去掉标识列,前面我们也提到过通过Data Annotations通用可以去掉主键默认标示属性
  47. modelBuilder.Entity<Employee>().Property(emp => emp.Title).HasMaxLength(30);//指定“Title”最大长度为30
  48. modelBuilder.Entity<Employee>().Property(emp => emp.BirthDate).IsRequired();//指定“BirthDate”为不可为空
  49. modelBuilder.Entity<Employee>().Property(emp => emp.Note).HasColumnName("Notes").HasColumnType("ntext");//指定“Note”对应列名为“Notes”,并指定数据类型
  50. modelBuilder.Entity<Employee>().Ignore(emp => emp.PhotoPath);//指定“PhotoPath”为非映射列
  51. //modelBuilder.Entity<Customer>().HasRequired(c=>c.Orders).WithMany().Map(m=>m.MapKey("CustomerOrder"));
  52. modelBuilder.Entity<Employee>().Property(emp => emp.Title).IsUnicode(true);//“Title”列是否支持Unicode编码
  53. modelBuilder.ComplexType<Name>().Property(n=>n.FirstName).HasMaxLength(50);//指定Name为复杂数据类型,并指定复杂类型中“FirstName”长度
  54. //modelBuilder.Entity<Employee>().Property(emp => emp.Name.LastName).HasMaxLength(20);//还可以通过这种方式指定复杂类型“Name”的“LastName”列的长度
  55. //modelBuilder.Entity<Employee>().Property(emp => emp.Address).IsConcurrencyToken();//指定“Address”进行并发控制,通常这一列我们知道为“TimeStamp”列而不是“Addree”这里只是为了说明可以标记其他列
  56. modelBuilder.Entity<Employee>().Property(emp => emp.TimeStamp).IsRowVersion();//通过指定“TimeStamp”进行并发版本控制
  57. /*下面代码演示EF中的继承关系实现*/
  58. //TPH默认支持,无需手动进行配置
  59. //TPT,只需要指定生成的表即可
  60. //modelBuilder.Entity<Employee>().ToTable("People", "Person");
  61. //modelBuilder.Entity<Worker>().ToTable("Worker", "Person");
  62. //modelBuilder.Entity<Retired>().ToTable("Retired", "Person");
  63. //TPC,只要子类中指定映射继承属性即可
  64. modelBuilder.Entity<Employee>().ToTable("People", "Person");
  65. modelBuilder.Entity<Worker>().Map(m => {
  66. m.MapInheritedProperties();
  67. m.ToTable("Worker", "Person");
  68. });
  69. modelBuilder.Entity<Retired>().Map(m =>
  70. {
  71. m.MapInheritedProperties();
  72. m.ToTable("Retired", "Person");
  73. });
  74. }
  75. }
  76. }
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Entity;
using CodeFirst.Entities;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity.ModelConfiguration.Conventions; namespace CodeFirst
{
public class OrderContext:DbContext
{
public OrderContext()
: base("CodeFirstDb")
{
Database.SetInitializer<OrderContext>(
new DropCreateDatabaseIfModelChanges<OrderContext>()
);
} public DbSet<Order> Orders
{
get;
set;
} public DbSet<Employee> Employees
{
get;
set;
} public DbSet<Worker> Workers
{
get;
set;
} public DbSet<Retired> Retireds
{
get;
set;
} protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
//modelBuilder.Entity<Employee>().ToTable("People", "Person");//指定“Employee”对应表名及架构 modelBuilder.Entity<Employee>().HasKey(emp => emp.No);//定义主键为“No”
//modelBuilder.Entity<Employee>().HasKey(emp => new { emp.No, emp.Title });//指定"No"和“Title”作为复合主键,使用Data Annotations无法做到 modelBuilder.Entity<Employee>().Property(emp => emp.No).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);//去掉标识列,前面我们也提到过通过Data Annotations通用可以去掉主键默认标示属性 modelBuilder.Entity<Employee>().Property(emp => emp.Title).HasMaxLength(30);//指定“Title”最大长度为30 modelBuilder.Entity<Employee>().Property(emp => emp.BirthDate).IsRequired();//指定“BirthDate”为不可为空 modelBuilder.Entity<Employee>().Property(emp => emp.Note).HasColumnName("Notes").HasColumnType("ntext");//指定“Note”对应列名为“Notes”,并指定数据类型 modelBuilder.Entity<Employee>().Ignore(emp => emp.PhotoPath);//指定“PhotoPath”为非映射列 //modelBuilder.Entity<Customer>().HasRequired(c=>c.Orders).WithMany().Map(m=>m.MapKey("CustomerOrder")); modelBuilder.Entity<Employee>().Property(emp => emp.Title).IsUnicode(true);//“Title”列是否支持Unicode编码 modelBuilder.ComplexType<Name>().Property(n=>n.FirstName).HasMaxLength(50);//指定Name为复杂数据类型,并指定复杂类型中“FirstName”长度
//modelBuilder.Entity<Employee>().Property(emp => emp.Name.LastName).HasMaxLength(20);//还可以通过这种方式指定复杂类型“Name”的“LastName”列的长度 //modelBuilder.Entity<Employee>().Property(emp => emp.Address).IsConcurrencyToken();//指定“Address”进行并发控制,通常这一列我们知道为“TimeStamp”列而不是“Addree”这里只是为了说明可以标记其他列
modelBuilder.Entity<Employee>().Property(emp => emp.TimeStamp).IsRowVersion();//通过指定“TimeStamp”进行并发版本控制 /*下面代码演示EF中的继承关系实现*/
//TPH默认支持,无需手动进行配置
//TPT,只需要指定生成的表即可
//modelBuilder.Entity<Employee>().ToTable("People", "Person");
//modelBuilder.Entity<Worker>().ToTable("Worker", "Person");
//modelBuilder.Entity<Retired>().ToTable("Retired", "Person");
//TPC,只要子类中指定映射继承属性即可
modelBuilder.Entity<Employee>().ToTable("People", "Person");
modelBuilder.Entity<Worker>().Map(m => {
m.MapInheritedProperties();
m.ToTable("Worker", "Person");
});
modelBuilder.Entity<Retired>().Map(m =>
{
m.MapInheritedProperties();
m.ToTable("Retired", "Person");
});
}
}
}

生成的表结构如下:

注意:尽管Fluent API功能更加强大,对于可以使用Data Annotations实现的功能我们推荐使用Data Annotations方式。

至此关于EF中默认约定及自定义配置的内容已经讨论结束了,关于EF中如何进行查询以及如何优化查询等更多内容敬请关注后面的文章。

【转】Entity Framework 5.0系列之约定配置的更多相关文章

  1. Entity Framework 5.0系列之约定配置

    Code First之所以能够让开发人员以一种更加高效.灵活的方式进行数据操作有一个重要的原因在于它的约定配置.现在软件开发越来复杂,大家也都试图将软件设计的越来越灵活,很多内容我们都希望是可配置的, ...

  2. Entity Framework 5.0系列之Code First数据库迁移

    我们知道无论是"Database First"还是"Model First"当模型发生改变了都可以通过Visual Studio设计视图进行更新,那么对于Cod ...

  3. Entity Framework 5.0系列之自动生成Code First代码

    在前面的文章中我们提到Entity Framework的"Code First"模式也同样可以基于现有数据库进行开发.今天就让我们一起看一下使用Entity Framework P ...

  4. 【转】Entity Framework 5.0系列之自动生成Code First代码

    在前面的文章中我们提到Entity Framework的“Code First”模式也同样可以基于现有数据库进行开发.今天就让我们一起看一下使用Entity Framework Power Tools ...

  5. (转)Entity Framework 5.0系列之自动生成Code First代码

    原文地址:http://www.cnblogs.com/kenshincui/archive/2013/08/29/3290527.html 在前面的文章中我们提到Entity Framework的“ ...

  6. Entity Framework 5.0系列之数据操作

    Entity Framework将概念模型中定义的实体和关系映射到数据源,利用实体框架可以将数据源返回的数据具体化为对象:跟踪对象所做的更改:并发处理:将对象更改传播到数据源等.今天我们就一起讨论如何 ...

  7. Entity Framework 5.0系列之EF概览

    概述 在开发面向数据的软件时我们常常为了解决业务问题实体.关系和逻辑构建模型而费尽心机,ORM的产生为我们提供了一种优雅的解决方案.ADO.NET Entity Framework是.NET开发中一种 ...

  8. Entity Framework 5.0系列之EF概览-三种编程方式

    概述 在开发面向数据的软件时我们常常为了解决业务问题实体.关系和逻辑构建模型而费尽心机,ORM的产生为我们提供了一种优雅的解决方案.ADO.NET Entity Framework是.NET开发中一种 ...

  9. Entity Framework 6.0 入门系列 第一篇

    Entity Framework 6.0 入门系列 第一篇 好几年前接触过一些ef感觉不是很好用,废弃.但是 Entity Framework 6.0是经过几个版本优化过的产物,性能和功能不断完善,开 ...

随机推荐

  1. js 浏览器上调试方法多样

    1.alert(111)       直接打印出 111 2.debugger        写在代码要调试的地方 3.直接在控制台 source 里找到要调试的代码打断点 4.console 常用的 ...

  2. C# XML序列化方法和常用特性

    /* C#对象XML序列化(一):序列化方法和常用特性 .Net Framework提供了对应的System.Xml.Seriazliation.XmlSerializer负责把对象序列化到XML,和 ...

  3. 画布跟js.oop

    <Canvas> 是HTML5中新出现的一个元素.就是可以通过  JS绘制图形. 画布(Canvas)是一个没有内容也没有边框的矩形区域.我们可以控制里面的每一个像素. 下面我们首先定义一 ...

  4. 原生JS实现音乐播放器!

      前  言            最近在复习JS,觉得音乐播放器是个挺有意思的东西,今天就来用我们最原生的JS写一个小小的音乐播放器~ 主要功能: 1.支持循环.随机播放 2.在播放的同时支持图片的 ...

  5. MxNet新前端Gluon模型转换到Symbol

    1. 导入各种包 from mxnet import gluon from mxnet.gluon import nn import matplotlib.pyplot as plt from mxn ...

  6. 【特效】手机端仿美团下拉菜单带遮罩层html+css+jquery

    写了一个手机端的下拉菜单,类似美团,用相对单位rem写的. 效果截图: 代码很简单,原理有点类似嵌套的选项卡,其中的难点在于弹出下拉菜单后,出现黑色半透明遮罩层,而且下层列表页面禁止滚动了.关键就是给 ...

  7. 基于HTML5和WebGL的碰撞测试

    这是公司大神写的一个放官网上给用户学习的例子,我一开始真的不知道这是在干嘛,就只是将三个形状图元组合在一起,然后可以同时旋转.放大缩小这个三个图形,点击"Animate"就能让中间 ...

  8. git 修改commit日期为之前的日期

    我在之前修改了一个文件,但是没有commit,现在我想要commit,日期为那天的日期 git commit --date="月 日 时间 年 +0800" -am "提 ...

  9. [译]ASP.NET Core 2.0 机密配置项

    问题 如何在ASP.NET Core 2.0中保存机密配置项(不用将其暴露给源代码管理器)? 答案 创建一个ASP.NET Core 2.0空项目,在项目节点上点击右键,并点击菜单项 - 管理用户机密 ...

  10. 关于session共享的解决方法

    当网站业务规模和访问量的逐步增大,原本由单台服务器.单个域名组成的网站架构可能已经无法满足发展需要 此时会购买更多的服务器,并且以频道化的方式启用多个二级子域名,然后根据业务功能将网站分别部署在独立的 ...