【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太多高级特性的时候谨慎,尽量不要和业务逻辑混合在一起,以免“不能自拔”。比如IgnoreShadowTable 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);
    }
}
posted @ 2022-04-17 12:01  .Neterr  阅读(235)  评论(0)    收藏  举报