【转】Entity Framework 5.0系列之约定配置

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默认生成数据表时将数据类名的复数形式作为表名,下面的代码就可以移除这个规则:

[csharp] view plaincopyprint?

  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类:

[csharp] view plaincopyprint?

  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类:

[csharp] view plaincopyprint?

  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类:

[csharp] view plaincopyprint?

  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类:

[csharp] view plaincopyprint?

  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的功能并且包含更多控制:

[csharp] view plaincopyprint?

  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类:

[csharp] view plaincopyprint?

  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类:

[csharp] view plaincopyprint?

  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类(添加了对应的属性):

[csharp] view plaincopyprint?

  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类(添加了对应的属性):

[csharp] view plaincopyprint?

  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类,定义了数据类之间的关系,主要是关系配置部分:

[csharp] view plaincopyprint?

  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类:

[csharp] view plaincopyprint?

  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类:

[csharp] view plaincopyprint?

  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;
        }
    }
}

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

[csharp] view plaincopyprint?

  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方式其实也十分简单,只需要配置基类及子类生成的表信息即可:

[csharp] view plaincopyprint?

  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”方法指定集成基类属性即可:

[csharp] view plaincopyprint?

  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中如何进行查询以及如何优化查询等更多内容敬请关注后面的文章。

时间: 2024-07-30 17:10:56

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

Entity Framework 5.0系列之约定配置

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

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

在前面的文章中我们提到Entity Framework的“Code First”模式也同样可以基于现有数据库进行开发.今天就让我们一起看一下使用Entity Framework Power Tools如何基于现有数据库生成数据类和数据库上下等. Entity Framework Power Tools 基于现有数据库生成POCO数据类和数据库上下文需要借助Visual Studio一个扩展插件-- Entity Framework Power Tools(一个Code First反向工程工具).

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

在前面的文章中我们提到Entity Framework的“Code First”模式也同样可以基于现有数据库进行开发.今天就让我们一起看一下使用Entity Framework Power Tools如何基于现有数据库生成数据类和数据库上下等. Entity Framework Power Tools 基于现有数据库生成POCO数据类和数据库上下文需要借助Visual Studio一个扩展插件-- Entity Framework Power Tools(一个Code First反向工程工具).

Entity Framework 5.0系列之EF概览

概述 在开发面向数据的软件时我们常常为了解决业务问题实体.关系和逻辑构建模型而费尽心机,ORM的产生为我们提供了一种优雅的解决方案.ADO.NET Entity Framework是.NET开发中一种由ADO.NET驱动的ORM框架,使用Entity Framework开发人员可以不必考虑数据的基础数据表和列,在处理数据时能够以更高的抽象级别进行工作,并能够以相对传统开发编写更少的代码来创建和维护应用程序. 我们知道面向对象的编程与数据存储系统的交换提出了一个难题:类结构通常同关系数据表组织结构

Entity Framework 5.0 Code First全面学习

目录(?)[+] 不贴图片了,太累. Code First 约定 借助 CodeFirst,可通过使用 C# 或Visual Basic .NET 类来描述模型.模型的基本形状可通过约定来检测.约定是规则集,用于在使用 Code First 时基于类定义自动配置概念模型.约定是在 System.Data.Entity.ModelConfiguration.Conventions 命名空间中定义的. 可通过使用数据注释或Fluent API 进一步配置模型.优先级是通过 Fluent API 进行

Entity Framework 5.0 Code First全面学习 (转)

原文地址:感谢原文作者 http://blog.csdn.net/gentle_wolf/article/details/14004345 不贴图片了,太累. Code First 约定 借助 CodeFirst,可通过使用 C# 或Visual Basic .NET 类来描述模型.模型的基本形状可通过约定来检测.约定是规则集,用于在使用 Code First 时基于类定义自动配置概念模型.约定是在 System.Data.Entity.ModelConfiguration.Convention

【转】Entity Framework技术导游系列开篇与热身

转自:http://blog.csdn.net/bitfan/article/details/12779517 Entity Framework走马观花 之 把握全局 ========================================= 这是一个系列文章 上一篇<Entity Framework技术导游系列开篇与热身 > ========================================= 在深入学习某项技术之前,应该努力形成对此技术的总体印象,并了解其基本原理,本

初次开发 ASP.NET vNext 续篇:云优化的概念、Entity Framework 7.0、目前性能还不够好

继续上一篇<开发 ASP.NET vNext 初步总结(使用Visual Studio 2014 CTP1)>之后, 关于云优化和版本控制: 我本想做一下MAC和LINUX的self-host测试,但是官方说运行环境的MONO版本至少需要3.4.1,我去年买了个表,至本文发布为止,你让我下地狱去找3.4.1吗,硬着头皮用3.4.0搞了一晚上,MAC一直停留在 httpapi.dll出错,Ubuntu Server 12.0.4 是不认其中的几个DLL包,具体哪几个也忘了,过段时间有了稳定版本再

Entity Framework 6 开发系列 目录

2014 年开始接触 Entity Framework 6 也快两年,用它已经沉淀了一个成熟架构,也用来开发了不少大大小小的产品和项目,直到这段时间,才真正有时间来回顾,重新学习它,为让大家更加了解Entity Framework 6,以及想把这两年中,开发过程中碰到的种种问题,跟大家分享,特此想做一个Entity Framework 6 开发系列的计划 第一篇:Entity Framework 简介 第二篇:Entity Framework CodeFirst & Model 映射 第三篇:E