一、概览
二、约定(默认)配置(仅列出最常用几项)
-
表名采用DbContext中的对应的DbSet的属性名。例如下图,创建一个Movie的表。
-
数据表列的名字采用实体类属性的名字,列的数据类型采用和实体类属性类型最兼容的类型。如下图1:
- 数据表列的可空性取决于对应实体类属性的可空性
- 名字为Id或“实体类型+Id”的属性默认为主键,如果主键是short int long类型,默认采用自增字段;如果主键为Guid类型,默认采用默认的Guid生成机制生成的主键。
三、FluentAPI使用方法
- 列的数据类型、表名,可以自行写配置文件覆盖默认(约定)配置,这种方式又称为FluentAPI:把配置文件写到单独的配置类中,优点是可以解耦,缺点是相对复杂
- 如何写配置文件:
1、针对每个具体实体类写一个实现IEntityTypeConfiguration<>抽象接口的Configure类的配置类,命名是实体类名+Config,具体配置内容(调用FluentAPI,FluentAPI常规用法详见下方六、FluentAPI)放在Configure方法中,例如针对Book类写一个配置类
2、在继承了DbContext类的OnModelCreating方法中中加载所有的配置。public class BookConfig : IEntityTypeConfiguration<Book> { public void Configure(EntityTypeBuilder<Book> builder) { builder.ToTable("T_Books"); //实体对应T_Books这张表,创建表用T_Books作为表名 覆盖DbSet的属性值 builder.Property(b=>b.Title).HasMaxLength(50).IsRequired(); //限定数据类型 覆盖实体类的数据类型 } }
3、public DbSet<Book> Books { get; set; },每个实体类都要定义DbSet<>类型的属性public class MyDdContext:DbContext { public DbSet<Book> Books { get; set; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { base.OnConfiguring(optionsBuilder); optionsBuilder.UseSqlServer("Data Source=DESKTOP-E3D2J8F\\SQLEXPRESS;" + "Initial Catalog=ImportExcelMVCTest;User ID=sa;Password=123456;TrustServerCertificate=true"); } //从指定的程序集里面加载所有config protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); //从当前程序集加载所有的IEntityTypeConfiguration modelBuilder.ApplyConfigurationsFromAssembly(this.GetType().Assembly); } }
四、Data Annotation
- 将配置以特性(Annotation)的形式标注在实体类中。
- Annotation方法也会更加简单,但是耦合性高,与FluentAPI相反。
- 可以映射到前端做数据校验,官方MVC入门Demo-添加校验使用了此功能用于表单提交数据的数据校验。
[Table("T_Books")] //设置实体类Book的表名
public class Book
{
[Required]
[MaxLength(22)]
public long Id { get; set; } //主键
public string? Title { get; set; } //标题
public DateTime PubTime { get; set; } //发布日期
public double Price { get; set; } //单价
}
五、Data Annotation与FluentAPI使用建议
- 二者可以混合使用,二者功能几乎重叠,但是从长远看建议只使用FluentAPI形式。
- 二者的用法微软EF Core官方文档已经将其做了对比说明,根据项目需要进行学习可以做到无缝衔接。
六、FluentAPI(仅部分,更多可详见官方文档)
-
视图与实体类映射 ToView
modelBuilder.Entity<Blog>().ToView("blogsView"); -
排除属性映射 Ignore
modelBuilder.Entity<Blog>().Ignore(b=>b.Name2);
Name2属性不会在数据表成生成字段 -
配置列名 HasColumnName
modelBuilder.Entity<Blog>().Property(b=>b.BlogId).HasColumnName("blog_id")
BlogId属性在数据表中字段名为blog_id -
配置列数据类型 HasColumnType
modelBuilder.Entity<Blog>().Property(b=>b.Title).HasColumnType("varchar(200)")
Title属性在数据表中数据类型为varchar(200) -
配置主键 HasKey
modelBuilder.Entity<Student>().HasKey(c=>c.Number);
默认把为名字为Id或者“实体类型+Id”的属性作为主键
HasKey来配置其他属性作为主键
支付复合主键,但是不建议使用 -
生成列的值 ValueGeneratedOnAdd
modelBuilder.Entity<Student>().Property(b=>b.Number).ValueGeneratedOnAdd(); -
设置默认值 HasDefaultValue
modelBuilder.Entity<Student>().Property(b=>b.Age).HasDefaultValue(6) -
索引 HasIndex
modelBuilder.Entity<Blog>().HasIndex(b=>b.Url); -
复合索引 HasIndex
modelBuilder.Entity<Blog>().HasIndex(p=>{p.FirstName,p.LasttName}); - 唯一索引 IsUnique()
- 聚合索引 IsClustered
总结:
- EF Core高级特性谨慎使用(例如:Ignore、Shadow、Table Splitting),容易提高系统复杂性,避免与业务逻辑混合在一起。
- 尽量在效率和复杂性之间找到一个平衡点,而并非一味的追求高效率而提高复杂性,或者一味的追求简单而降低效率。
- Dapper简单、生产效率低,EF Core复杂、生产效率高,基于第二条的原则要让EF Core的使用Dapper化
七、FluentAPI语法糖
-
方法重载,多种调用方式
以下两种方法作用一致,建议使用第一种,因为如果出现属性名(Title)写错在编译层面就会直接报错builder.Property(b => b.Title).HasDefaultValue("Hello"); //第一种 builder.Property("Title").HasDefaultValue("Hello"); //第二种
-
链式编程风格
builder.Property(b => b.Title).HasDefaultValue("Hello").HasColumnName("_title").HasMaxLength(20);
-
错误的链式编程风格
以下代码会报错
HasIndex()返回值是IndexBuilder<TEntity>,而Ignore()方法是EntityTypeBuilder<>类的方方法。builder.ToTable("T_Books").HasIndex(b => b.Title).Ignore(b => b.PubTime);