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 的默认架构名:dbo
modelBuilder.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);