ASP.NET Core 使用 Entity Framework 创建模型

创建模型就是创建和数据库表对应的实体类,以及实体类之间的关系,也就是数据库表之间的关系。要想更好地掌握 Entity Framework 模型的创建,需要对数据库知识有一定的熟悉度,因为创建模型的目的就是为了和数据库打交道,离不开原有的数据库架构体系。

我们知道,数据库表的字段除了名称外,还包括诸如类型、长度、是否为空、主键、索引、外键等等属性。模型中必然也需要对应功能才行,实现的方法有两种。

实体中配置

比如我们有一个员工表 Employee

  1. namespace MyFirstCoreWebApp.Models
  2. {
  3. [Table("Employee")]
  4. public class Employee
  5. {
  6. public int ID { get; set; }
  7. [Required]
  8. [MaxLength(50)]
  9. public string Name { get; set; }
  10. [DataType(DataType.Date)]
  11. public DateTime EntryTime { get; set; }
  12. }
  13. }

上述代码的实体类 Employee,有三个属性:ID、Name、EntryTime,对应了表的三个字段外,特性分别代表了:

  • 表的名称
  • 字段 Name 不能为 NULL
  • 字段 Name 的长度为 50
  • 字段 ExpireDate 的数据类型为 Date

fluent API 配置

这种方式来配置不需要在实体类附加过多的特性,能保持实体类的纯净性,本文将使用这种方式来配置模型。使用 fluent API 需要在数据库上下文类 DbContext 的派生类中重写 OnModelCreating 方法:

  1. namespace MyFirstCoreWebApp.Models
  2. {
  3. public class MyFirstCoreWebAppDbContext : DbContext
  4. {
  5. public MyFirstCoreWebAppDbContext(DbContextOptions<MyFirstCoreWebAppDbContext> options) : base(options)
  6. {
  7. }
  8. protected override void OnModelCreating(ModelBuilder modelBuilder)
  9. {
  10. modelBuilder.HasDefaultSchema("test");//也可以不命名,使用SqlServer 的默认架构名:dbo
  11. modelBuilder.Entity<Employee>(prod=> {
  12. prod.Property(p => p.ID).IsRequired();//必填
  13. prod.Property(p => p.Name).HasMaxLength(50).IsRequired();//长度50,必填
  14. prod.Property(p => p.EntryDate).HasColumnType("Date");//类型为 Date
  15. });
  16. modelBuilder.Entity<Employee>().HasKey(o => o.ID);//定义主键
  17. //modelBuilder.Entity<Employee>().HasKey(o => new { o.ID, o.Name});//定义联合主键
  18. //modelBuilder.Entity<Employee>().Property(o => o.ID).ValueGeneratedNever();//自增列
  19. modelBuilder.Entity<Employee>().HasIndex(od => od.ID);//定义索引
  20. //modelBuilder.Entity<Employee>().HasIndex(od => new { od.ID, od.Name });//定义联合索引
  21. }
  22. }
  23. }

如果表很多时,这样配置会使得 OnModelCreating 方法的代码过多,可以分开配置不同的表,只需要实现接口 IEntityTypeConfiguration<TEntity> 即可:

  1. namespace MyFirstCoreWebApp.Models
  2. {
  3. public class EmployeeEntityTypeConfiguration: IEntityTypeConfiguration<Employee>
  4. {
  5. public void Configure(EntityTypeBuilder<Employee> builder)
  6. {
  7. builder.ToTable("Employee");
  8. builder.HasIndex(p => p.ID);
  9. builder.Property(p => p.Name).HasMaxLength(50).IsRequired();
  10. builder.Property(p => p.EntryDate).HasColumnType("Date");
  11. }
  12. }
  13. }

Employee 在另一个类中配置,然后在 OnModelCreating 调用 Configure 方法:

  1. new EmployeeEntityTypeConfiguration().Configure(modelBuilder.Entity<Employee>());

定义表关系

还可以定义表之间的关系,比如我们还有一张员工日志表 EmployeeLogEmployee 表与它的关系将是一对多,即 Employee 表的一条记录对应 EmployeeLog 表的多条记录,反之,EmployeeLog 表的一条记录只对应Employee 表的一条记录。在关系数据库中,这由外键约束表示。

在定义 Employee 实体类和 EmployeeLog 实体类时,还定义了导航属性,Employee 类的导航属性为:

  1. public List<EmployeeLog> EmployeeLogs { get; set; }

EmployeeLog 的导航属性为:

  1. public Employee Employee { get; set; }

接下来 fluent API 配置它们之间的关系:

  1. modelBuilder.Entity<Employee>().HasMany(o => o.EmployeeLog).WithOne(od => od.ID);

或者通过 EmployeeLog 来配置

  1. modelBuilder.Entity<EmployeeLog>().HasOne(d => d.Employee).WithMany(d => d.EmployeeLogs);
注意:看 HasManyWithOneHasOneWithMany 之间的关系,就是 1 对多的关系。还有 1 对 1 的关系:HasOneWithOne;多对多的关系:HasManyWithMany,可根据实际情况去定义。有的只有单个导航属性,调用没有参数的重载方法即可。

关系还有一个重要的概念:级联删除,比如 EmployeeEmployeeLog 之间的关系,当删除 Employee 表的数据时, EmployeeLog 表对应的 EmployeeID 数据也会被删除。

如果不希望级联删除,则需要这样配置:

  1. modelBuilder.Entity<EmployeeLog>().HasOne(d => d.Employee).WithMany(d => d.EmployeeLogs).OnDelete(DeleteBehavior.NoAction);

分类导航