ASP.NET Core 使用 Entity Framework 创建模型
创建模型就是创建和数据库表对应的实体类,以及实体类之间的关系,也就是数据库表之间的关系。要想更好地掌握 Entity Framework 模型的创建,需要对数据库知识有一定的熟悉度,因为创建模型的目的就是为了和数据库打交道,离不开原有的数据库架构体系。
我们知道,数据库表的字段除了名称外,还包括诸如类型、长度、是否为空、主键、索引、外键等等属性。模型中必然也需要对应功能才行,实现的方法有两种。
实体中配置
比如我们有一个员工表 Employee:
namespace MyFirstCoreWebApp.Models{[Table("Employee")]public class Employee{public int ID { get; set; }[Required][MaxLength(50)]public string Name { get; set; }[DataType(DataType.Date)]public DateTime EntryTime { get; set; }}}
上述代码的实体类 Employee,有三个属性:ID、Name、EntryTime,对应了表的三个字段外,特性分别代表了:
- 表的名称
- 字段 Name 不能为
NULL - 字段 Name 的长度为 50
- 字段 ExpireDate 的数据类型为
Date
fluent API 配置
这种方式来配置不需要在实体类附加过多的特性,能保持实体类的纯净性,本文将使用这种方式来配置模型。使用 fluent API 需要在数据库上下文类 DbContext 的派生类中重写 OnModelCreating 方法:
namespace MyFirstCoreWebApp.Models{public class MyFirstCoreWebAppDbContext : DbContext{public MyFirstCoreWebAppDbContext(DbContextOptions<MyFirstCoreWebAppDbContext> options) : base(options){}protected override void OnModelCreating(ModelBuilder modelBuilder){modelBuilder.HasDefaultSchema("test");//也可以不命名,使用SqlServer 的默认架构名:dbomodelBuilder.Entity<Employee>(prod=> {prod.Property(p => p.ID).IsRequired();//必填prod.Property(p => p.Name).HasMaxLength(50).IsRequired();//长度50,必填prod.Property(p => p.EntryDate).HasColumnType("Date");//类型为 Date});modelBuilder.Entity<Employee>().HasKey(o => o.ID);//定义主键//modelBuilder.Entity<Employee>().HasKey(o => new { o.ID, o.Name});//定义联合主键//modelBuilder.Entity<Employee>().Property(o => o.ID).ValueGeneratedNever();//自增列modelBuilder.Entity<Employee>().HasIndex(od => od.ID);//定义索引//modelBuilder.Entity<Employee>().HasIndex(od => new { od.ID, od.Name });//定义联合索引}}}
如果表很多时,这样配置会使得 OnModelCreating 方法的代码过多,可以分开配置不同的表,只需要实现接口 IEntityTypeConfiguration<TEntity> 即可:
namespace MyFirstCoreWebApp.Models{public class EmployeeEntityTypeConfiguration: IEntityTypeConfiguration<Employee>{public void Configure(EntityTypeBuilder<Employee> builder){builder.ToTable("Employee");builder.HasIndex(p => p.ID);builder.Property(p => p.Name).HasMaxLength(50).IsRequired();builder.Property(p => p.EntryDate).HasColumnType("Date");}}}
Employee 在另一个类中配置,然后在 OnModelCreating 调用 Configure 方法:
new EmployeeEntityTypeConfiguration().Configure(modelBuilder.Entity<Employee>());
定义表关系
还可以定义表之间的关系,比如我们还有一张员工日志表 EmployeeLog,Employee 表与它的关系将是一对多,即 Employee 表的一条记录对应 EmployeeLog 表的多条记录,反之,EmployeeLog 表的一条记录只对应Employee 表的一条记录。在关系数据库中,这由外键约束表示。
在定义 Employee 实体类和 EmployeeLog 实体类时,还定义了导航属性,Employee 类的导航属性为:
public List<EmployeeLog> EmployeeLogs { get; set; }
EmployeeLog 的导航属性为:
public Employee Employee { get; set; }
接下来 fluent API 配置它们之间的关系:
modelBuilder.Entity<Employee>().HasMany(o => o.EmployeeLog).WithOne(od => od.ID);
或者通过 EmployeeLog 来配置
modelBuilder.Entity<EmployeeLog>().HasOne(d => d.Employee).WithMany(d => d.EmployeeLogs);
HasMany、WithOne 和 HasOne、WithMany 之间的关系,就是 1 对多的关系。还有 1 对 1 的关系:HasOne、WithOne;多对多的关系:HasMany、WithMany,可根据实际情况去定义。有的只有单个导航属性,调用没有参数的重载方法即可。关系还有一个重要的概念:级联删除,比如 Employee 和 EmployeeLog 之间的关系,当删除 Employee 表的数据时, EmployeeLog 表对应的 EmployeeID 数据也会被删除。
如果不希望级联删除,则需要这样配置:
modelBuilder.Entity<EmployeeLog>().HasOne(d => d.Employee).WithMany(d => d.EmployeeLogs).OnDelete(DeleteBehavior.NoAction);