02020403 EF Core基础03-Fluent API、Data Annotation、两种配置的选择
1. FluentAPI哪些不该用(视频3-4)
1.1 约定配置
主要规则:
1:表名采用DbContext中的对应的DbSet的属性名。
2:数据表列的名字采用实体类属性的名字,列的数据类型采用和实体类属性类型最兼容的类型。
3:数据表列的可空性取决于对应实体类属性的可空性。
4:名字为Id的属性为主键,如果主键为short, int 或者 long类型,则默认采用自增字段,如果主键为Guid类型,则默认采用默认的Guid生成机制生成主键值。
1.2 DbSet属性名作为数据表名
// Dog.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace EFCoreDemo
{public class Dog{public long Id { get; set; } //主键public string Name { get; set; }//标题}
}
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
// DbContext.cs
using Microsoft.EntityFrameworkCore;namespace EFCoreDemo
{class MyDbContext : DbContext{public DbSet<Book> Books { get; set; }public DbSet<Person> Persons { get; set; }public DbSet<Dog> Dogs { get; set; } // @1protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder){string connStr = "Server=.;Database=CoreDataDB;Trusted_Connection=True;MultipleActiveResultSets=true";optionsBuilder.UseSqlServer(connStr);}protected override void OnModelCreating(ModelBuilder modelBuilder){base.OnModelCreating(modelBuilder);modelBuilder.ApplyConfigurationsFromAssembly(this.GetType().Assembly);}}
}说明:在@1处,因为Dog实体类没有配置DogConfig配置类,此时数据库中创建的表名称为Dogs,与DbSet属性对应。
1.3 两种配置方式
1、Data Annotation
把配置以特性(Annotation)的形式标注在实体类中。
[Table("T_Books")]
public class Book
{
}
优点:简单;缺点:耦合。
2、Fluent API
builder.ToTable("T_Books");
把配置写到单独的配置类中。
缺点:复杂;优点:解耦
3、大部分功能重叠。可以混用,但是不建议混用。
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
// 使用Fluent API存到不同的数据库
if(mysql)
{builder.ToTable("T_Persons");
}
else(sql server)
{builder.ToTable("Persons")
}说明:
1. Fluent API形式可以解耦,而Data Annotation无法实现。
2. 实际开发中,推荐使用Fluet API的形式。因为开发中,各种奇葩需求层出不绝。
3. 本课程除了特殊的地方,老师采用Fluent API形式配置数据库。
1.4 Fluent API配置形式
- 在02020401和02020402中创建的数据表采用的是Fluent API的形式。
1.5 Data Annotation配置形式
// Cat.cs
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;namespace EFCoreDemo
{[Table("T_Cats")]class Cat{public long Id { get; set; } //主键[Required] // 不可为空[MaxLength(22)] // 最大长度22public string Name { get; set; }//标题}
}
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
// MyDbContext.cs
using Microsoft.EntityFrameworkCore;namespace EFCoreDemo
{class MyDbContext : DbContext{public DbSet<Cat> Cats { get; set; }protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder){string connStr = "Server=.;Database=CoreDataDB;Trusted_Connection=True;MultipleActiveResultSets=true";optionsBuilder.UseSqlServer(connStr);}protected override void OnModelCreating(ModelBuilder modelBuilder){base.OnModelCreating(modelBuilder);modelBuilder.ApplyConfigurationsFromAssembly(this.GetType().Assembly);}}
}
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
// 程序包资源管理器控制台:
PM> add-migration add_cat
Build started...
Build succeeded.
To undo this action, use Remove-Migration.
PM> update-database
Build started...
Build succeeded.
Applying migration '20250919133837_add_cat'.
Done.
PM> 说明:此时会生成T_Cats,并有设定的配置。
- 执行上述代码,在SSMS中生成的表如下

1.6 课外探讨(仅供参考)
- A、B两个技术,如果A比B适度且合理复杂,那么尽量选A。
- 要尽量选稍微复杂一点的。
- 做项目会发现,一开始简单的,最后会把你坑的要死。
2. Fluent API基础特性
1、视图与实体类映射:modelBuilder.Entity<Blog>().ToView("blogsView");
2、排除属性映射:
modelBuilder.Entity<Blog>().Ignore(b => b. Name2);
3、配置列名:默认是实体类的属性名,也可以配置为其它名称。
modelBuilder.Entity<Blog>().Property(b =>b.BlogId).HasColumnName("blog_id");
4、配置列数据类型:
builder.Property(e => e.Title) .HasColumnType("varchar(200)")
5、配置主键
默认把名字为Id或者“实体类型+Id“的属性作为主键,可以用HasKey()来配置其他属性作为主键。modelBuilder.Entity<Student>().HasKey(c => c.Number);
支持复合主键,但是不建议使用。
6、生成列的值
modelBuilder.Entity<Student>().Property(b => b.Number).ValueGeneratedOnAdd();
7、可以用HasDefaultValue()为属性设定默认值
modelBuilder.Entity<Student>().Property(b => b.Age).HasDefaultValue(6);
8、索引
modelBuilder.Entity<Blog>().HasIndex(b => b.Url);
复合索引
modelBuilder.Entity<Person>().HasIndex(p => new { p.FirstName, p.LastName });
唯一索引:IsUnique();聚集索引:IsClustered()
9... 用EF Core太多高级特性的时候谨慎,尽量不要和业务逻辑混合在一起,以免“不能自拔”。比如Ignore、Shadow、Table Splitting等……说明:
1. Dapper简单、入门快、生产效率低。
2. EF Core复杂、生产效率高。但是不要用EF Core很多复杂的高级特性,要让EF Core用的更像Dapper一些。
2.1 让EF Core生产效率稍微低一点,但是更加简单。
2.2 不要陷入EF Core复杂的高级特性中不能自拔。
3. 根据实际开发经验,找到Dapper和EF Core之间的平衡点。
3. Fluent API中的方法(视频3-5)
3.1 Fluent API众多方法
Fluent API中很多方法都有多个重载方法。比如HasIndex、Property()。
把Number属性定义为索引,下面两种方法都可以:
builder.HasIndex("Number");
builder.HasIndex(b=>b.Number);推荐使用HasIndex(b=>b.Number)、Property(b => b.Number)这样的写法,因为这样利用的是C#的强类型检查机制
3.2 Property的重载方法
// Person.cs
namespace EFCoreDemo
{public class Person{public long Id { get; set; }public long Name { get; set; }public int Age { get; set; } public string BirthPlace { get; set; } public double? Salary { get; set; } }
}
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
// PersonConfig.cs
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;namespace EFCoreDemo
{class PersonConfig : IEntityTypeConfiguration<Person>{public void Configure(EntityTypeBuilder<Person> builder){builder.Property(b => b.Name).HasDefaultValue("Qinway"); // @1 Lambda表达式写法builder.Property("Name").HasDefaultValue("YZK"); // @2 采用属性名的写法builder.HasKey(b => b.Id); // 推荐的Lambda写法builder.HasKey("Id"); // 不推荐的属性写法}}
}说明:
1. 推荐用@1处的写法。这样可以通过Tab自动完成,同时还可以使用类型检查机制。
2. 比如在@1处,b.Name写成了b.Name3,编译器会报错。而在@2处"Name"写成了"Name3",编译器不会报错。
3.3 Fluent API的对比写法
// 标准写法:3条语句
builder.ToTable("T_Books");
builder.HasIndex(b => b.Title);
builder.Ignore(b => b.PubTime);
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
// 正确写法:2条语句
builder.ToTable("T_Books").HasIndex(b => b.Title);
builder.Ignore(b => b.PubTime);<= 上述写法与下面写法等价 =>EntityTypeBuilder<Book> etbBook = builder.ToTable("T_Books");
etbBook.HasIndex(b => b.Title);
builder.Ignore(b => b.PubTime);说明
1. Fluent API不管怎么写,必须符合C#的语法。
2. ToTable方法的返回值类型为EntityTypeBuilder类型,HasIndex方法是EntityTypeBuilder类的方法,因此可以.下去。
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
// 错误写法:1条语句
builder.ToTable("T_Books").HasIndex(b => b.Title).Ignore(b => b.PubTime);说明:
1. Ignore()不是HasIndex的属性或者方法,因此不能.了。
2. 可以调整Ignore()和HasIndex()的顺序,可能不会报错。
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
综上:
1. 对于表的配置写一行。
2. 对于属性写一行。
3. 对于索引写一行。
4. Data Annotation和Fluent API的选择
1、Data Annotation 、Fluent API大部分功能重叠。可以混用,但是不建议混用。
2、有人建议混用,即用了Data Annotation 的简单,又用到Fluent API的强大,而且实体类上标注的[MaxLength(50)]、[Required]等标注可以被ASP.NET Core中的验证框架等复用。我为什么不建议混用。
3、我和业界很多人都倾向只使用Fluent API。本课以讲解Fluent API为主(尽量用约定),如果项目强制用Data Annotation 请翻文档,知识都是通用的。
结尾
书籍:ASP.NET Core技术内幕与项目实战
视频:https://www.bilibili.com/video/BV1pK41137He
著:杨中科
ISBN:978-7-115-58657-5
版次:第1版
发行:人民邮电出版社
※敬请购买正版书籍,侵删请联系85863947@qq.com※
※本文章为看书或查阅资料而总结的笔记,仅供参考,如有错误请留言指正,谢谢!※