【EF Core】Fluent API
Entity Framework (EF) 的 Fluent API 是一种基于代码的配置方式,用于灵活地定义实体类与数据库表之间的映射关系。相比数据注解(Data Annotations),Fluent API 提供了更强大的配置能力,不修改实体类的情况下指定配置,优先级更高(覆盖数据注解和默认约定)
配置范围
- 模型配置:默认架构、序列、函数等(如 HasDefaultSchema)。
- 实体配置:主键、表名、索引、关系(一对一、一对多、多对多)。
- 属性配置:列名、数据类型、默认值、是否可为空等。
基本用法
方式1、在 DbContext 的 OnModelCreating 方法中使用 ModelBuilder 配置:
protected override void OnModelCreating(ModelBuilder modelBuilder) {
// 配置实体
modelBuilder.Entity<Country>(entity => {
entity.HasKey(c => c.PId); // 主键
entity.Property(c => c.Name)
.HasColumnName("CountryName") // 列名
.HasDefaultValue("USA") // 默认值
.IsRequired(); // 非空
entity.Ignore(c => c.Population); // 忽略映射
});
// 配置一对多关系
modelBuilder.Entity<Parent>()
.HasMany(p => p.Children)
.WithOne(c => c.Parent);
}
方式2、每个模型类实现IEntityTypeConfiguration
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.ApplyConfigurationsFromAssembly(this.GetType().Assembly);
//modelBuilder.ApplyConfigurationsFromAssembly(typeof(UserInfo).Assembly);
}
public class UserInfoEntityConfig : IEntityTypeConfiguration<UserInfo>
{
public void Configure(EntityTypeBuilder<UserInfo> builder)
{
builder.ToTable("T_UserInfo2");
builder.Property(e => e.Id).HasMaxLength(50).IsRequired();
builder.Property(e => e.Name).HasColumnType("varchar(100)").IsRequired();
builder.Property(e => e.Email).HasColumnType("varchar(100)").IsRequired(false);
builder.Property(e => e.Phone).HasColumnType("varchar(100)").IsRequired(false);
}
}
Fluent API
- 表配置
modelBuilder.Entity<UserInfo>()
.ToTable("T_UserInfos","schemaName")//指定表名和schema(只有sqlserver有schema),schema默认dbo
.HasComment("用户表")//表说明
.Ignore<UserInfo>();//如果表存在会删除表
- 字段配置
modelBuilder.Entity<Blog>().Property(b =>b.BlogId)
.HasColumnName("blog_id")//字段名
.Ignore(b => b.Name2)//排除字段映射
.HasColumnType("varchar(200)")//字段数据类型
.HasDefaultValue(6)//默认值
.HasDefaultValueSql("GETDATE()"); //默认值, SQL Server当前时间
.HasMaxLength(300)//最大长度
.IsRequired()//非空
.HasComment("用户名");//注释
- 配置主键
默认把名字为Id或者“实体类型+Id“的属性作为主键,可以用HasKey()来配置其他属性作为主键。
modelBuilder.Entity<Student>().HasKey(c => c.Number);
//联合主键
modelBuilder.Entity<UserInfo>()
.HasKey(u => new { u.Id, u.Name }).HasName("ID");
- 外键(不规则的外键字段名才需要自己指定外键)
modelBuilder.Entity<Comment>().HasOne(p => p.Blog).WithMany(b => b.Comments).HasForeignKey(p => p.BId);
- 设置索引
modelBuilder.Entity<Comment>()
.HasIndex(c => c.Content)
.HasDatabaseName("idx_content");//索引名
//复合索引
modelBuilder.Entity<Person>().HasIndex(p => new { p.FirstName, p.LastName });
//唯一索引
modelBuilder.Entity<Blog>().HasIndex(b => b.Url).IsUnique();
//聚集索引
modelBuilder.Entity<Blog>().HasIndex(b => b.CreateTime).IsClustered();
- 将私有成员映射成表字段
public record User
{
private string? passwordHash;
}
builder.Property("passwordHash");
- 视图与实体类映射
将实体类Blog映射到数据库中的一个视图(view),而不是默认的表(table)
modelBuilder.Entity<Blog>().ToView("blogsView");
- 将DDD值对象映射到当前实体对应的表中
public record User : IAggregateRoot
{
public PhoneNumber PhoneNumber { get; private set; }//手机号
}
builder.OwnsOne(x => x.PhoneNumber, nb => {
nb.Property(x => x.RegionCode).HasMaxLength(5).IsUnicode(false);
nb.Property(x => x.Number).HasMaxLength(20).IsUnicode(false);
});
用EF Core太多高级特性的时候谨慎,尽量不要和业务逻辑混合在一起,以免“不能自拔”。比如
Ignore、Shadow、Table Splitting……
全局过滤器
EF Core 会自动将这个查询筛选器应用于涉及这个实体类型的所有 LINQ 查询。
场景:软删除、多租户。
配置全局过滤器:
modelBuilder.HasQueryFilter(b=>b.IsDeleted==false);
之后所有的查询都会自动带上IsDeleted=0这个条件
忽略全局过滤器:
ctx.Books.IgnoreQueryFilters().Where(b => b.Title.Contains("o")).ToArray();
如果过滤条件需要动态数据(如当前用户身份),可以通过 DbContext 的构造函数注入服务
public class AppDbContext : DbContext
{
private readonly IUserContext _userContext;
public AppDbContext(DbContextOptions<AppDbContext> options, IUserContext userContext)
: base(options) => _userContext = userContext;
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>().HasQueryFilter(b =>
!b.IsDeleted && b.TenantId == _userContext.CurrentTenantId);
}
}

浙公网安备 33010602011771号