EF Core Fluent API:强大而灵活的数据模型配置
引言
你是否曾经在使用Entity Framework Core时,为了精确控制数据库映射而苦恼?是否觉得数据注解(Data Annotations)在某些复杂场景下力不从心?EF Core Fluent API正是为了解决这些问题而设计的强大工具。它提供了比数据注解更丰富、更灵活的配置选项,让你能够完全掌控实体模型与数据库之间的映射关系。
通过本文,你将掌握:
- Fluent API的核心概念和优势
- 实体、属性和关系的完整配置方法
- 高级配置技巧和最佳实践
- 实际应用场景和代码示例
Fluent API 概述
什么是Fluent API?
Fluent API(流畅接口)是EF Core中用于配置数据模型的一种编程方式。它采用链式方法调用的设计模式,让配置代码更加清晰、易读。与数据注解相比,Fluent API提供了更细粒度的控制能力。
为什么选择Fluent API?
核心配置方法详解
1. 实体配置
基本实体配置
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// 配置实体映射的表名
modelBuilder.Entity<Blog>().ToTable("Blogs");
// 配置架构
modelBuilder.Entity<Blog>().ToTable("Blogs", "blogging");
// 排除实体
modelBuilder.Ignore<BlogMetadata>();
}
主键配置
modelBuilder.Entity<Order>()
.HasKey(o => o.OrderId); // 单字段主键
modelBuilder.Entity<OrderDetail>()
.HasKey(od => new { od.OrderId, od.ProductId }); // 复合主键
modelBuilder.Entity<ViewOnlyEntity>()
.HasNoKey(); // 无主键实体(仅用于查询)
2. 属性配置
基本属性配置
modelBuilder.Entity<Blog>()
.Property(b => b.Url)
.IsRequired() // 非空约束
.HasMaxLength(500) // 最大长度
.HasColumnName("BlogUrl") // 列名映射
.HasDefaultValue("https://") // 默认值
.HasComment("博客链接地址"); // 列注释
数据类型和转换器
// 配置数据类型
modelBuilder.Entity<Product>()
.Property(p => p.Price)
.HasColumnType("decimal(18,2)");
// 使用值转换器
modelBuilder.Entity<Blog>()
.Property(b => b.Status)
.HasConversion(
v => v.ToString(),
v => (BlogStatus)Enum.Parse(typeof(BlogStatus), v)
);
计算列和默认值
modelBuilder.Entity<Order>()
.Property(o => o.CreatedDate)
.HasDefaultValueSql("GETDATE()"); // SQL Server默认值
modelBuilder.Entity<Order>()
.Property(o => o.TotalAmount)
.HasComputedColumnSql("[Quantity] * [UnitPrice]"); // 计算列
3. 关系配置
一对一关系
modelBuilder.Entity<Blog>()
.HasOne(b => b.BlogImage)
.WithOne(i => i.Blog)
.HasForeignKey<BlogImage>(i => i.BlogId);
一对多关系
modelBuilder.Entity<Blog>()
.HasMany(b => b.Posts)
.WithOne(p => p.Blog)
.HasForeignKey(p => p.BlogId)
.OnDelete(DeleteBehavior.Cascade); // 级联删除
多对多关系
modelBuilder.Entity<Post>()
.HasMany(p => p.Tags)
.WithMany(t => t.Posts)
.UsingEntity<PostTag>(
j => j.HasOne(pt => pt.Tag).WithMany().HasForeignKey(pt => pt.TagId),
j => j.HasOne(pt => pt.Post).WithMany().HasForeignKey(pt => pt.PostId),
j => j.HasKey(pt => new { pt.PostId, pt.TagId })
);
4. 索引配置
// 创建唯一索引
modelBuilder.Entity<User>()
.HasIndex(u => u.Email)
.IsUnique();
// 复合索引
modelBuilder.Entity<Post>()
.HasIndex(p => new { p.BlogId, p.CreatedDate })
.IsDescending(false, true); // BlogId升序,CreatedDate降序
// 包含列索引(SQL Server)
modelBuilder.Entity<Post>()
.HasIndex(p => p.Title)
.IncludeProperties(p => new { p.Content, p.AuthorId });
高级配置技巧
1. 继承映射策略
// TPH(Table Per Hierarchy)策略
modelBuilder.Entity<Blog>()
.HasDiscriminator<string>("BlogType")
.HasValue<TechnicalBlog>("Technical")
.HasValue<PersonalBlog>("Personal");
// TPT(Table Per Type)策略
modelBuilder.Entity<TechnicalBlog>().ToTable("TechnicalBlogs");
modelBuilder.Entity<PersonalBlog>().ToTable("PersonalBlogs");
// TPC(Table Per Concrete)策略
modelBuilder.Entity<TechnicalBlog>().UseTpcMappingStrategy();
modelBuilder.Entity<PersonalBlog>().UseTpcMappingStrategy();
2. 复杂类型配置
modelBuilder.Entity<Order>()
.OwnsOne(o => o.ShippingAddress, address =>
{
address.Property(a => a.Street).HasMaxLength(100);
address.Property(a => a.City).HasMaxLength(50);
address.Property(a => a.ZipCode).HasMaxLength(10);
});
modelBuilder.Entity<Customer>()
.OwnsMany(c => c.Addresses, address =>
{
address.WithOwner().HasForeignKey("CustomerId");
address.Property<int>("Id");
address.HasKey("Id");
});
3. 全局配置
// 配置默认架构
modelBuilder.HasDefaultSchema("sales");
// 配置全局查询过滤器
modelBuilder.Entity<Blog>()
.HasQueryFilter(b => !b.IsDeleted);
// 配置并发令牌
modelBuilder.Entity<Blog>()
.Property(b => b.Timestamp)
.IsRowVersion();
配置的组织和管理
使用 IEntityTypeConfiguration
public class BlogConfiguration : IEntityTypeConfiguration<Blog>
{
public void Configure(EntityTypeBuilder<Blog> builder)
{
builder.ToTable("Blogs");
builder.HasKey(b => b.BlogId);
builder.Property(b => b.Url).HasMaxLength(500);
builder.HasMany(b => b.Posts).WithOne(p => p.Blog);
}
}
// 在DbContext中应用配置
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfiguration(new BlogConfiguration());
}
自动应用所有配置
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfigurationsFromAssembly(
typeof(ApplicationDbContext).Assembly);
}
实际应用场景
场景1:电子商务系统
// 产品实体配置
modelBuilder.Entity<Product>(entity =>
{
entity.ToTable("Products", "catalog");
entity.HasKey(p => p.ProductId);
entity.Property(p => p.Name)
.HasMaxLength(200)
.IsRequired();
entity.Property(p => p.Price)
.HasColumnType("decimal(18,2)")
.HasDefaultValue(0);
entity.HasIndex(p => p.CategoryId);
entity.HasIndex(p => new { p.Name, p.BrandId });
});
// 订单实体配置
modelBuilder.Entity<Order>(entity =>
{
entity.ToTable("Orders", "sales");
entity.HasKey(o => o.OrderId);
entity.Property(o => o.OrderDate)
.HasDefaultValueSql("GETDATE()");
entity.Property(o => o.TotalAmount)
.HasComputedColumnSql("[SubTotal] + [TaxAmount] - [DiscountAmount]");
entity.HasMany(o => o.OrderItems)
.WithOne(oi => oi.Order)
.OnDelete(DeleteBehavior.Cascade);
});
场景2:博客系统
// 博客文章配置
modelBuilder.Entity<Post>(entity =>
{
entity.HasKey(p => p.PostId);
entity.Property(p => p.Title)
.HasMaxLength(200)
.IsRequired();
entity.Property(p => p.Slug)
.HasMaxLength(250)
.IsRequired();
entity.HasIndex(p => p.Slug)
.IsUnique();
entity.HasIndex(p => new { p.BlogId, p.PublishDate })
.IsDescending(false, true);
// 软删除支持
entity.HasQueryFilter(p => !p.IsDeleted);
});
// 标签配置
modelBuilder.Entity<Tag>(entity =>
{
entity.HasKey(t => t.TagId);
entity.Property(t => t.Name)
.HasMaxLength(50)
.IsRequired();
entity.HasIndex(t => t.Name)
.IsUnique();
// 多对多关系
entity.HasMany(t => t.Posts)
.WithMany(p => p.Tags)
.UsingEntity<PostTag>(
j => j.HasOne(pt => pt.Post).WithMany().HasForeignKey(pt => pt.PostId),
j => j.HasOne(pt => pt.Tag).WithMany().HasForeignKey(pt => pt.TagId),
j => j.HasKey(pt => new { pt.PostId, pt.TagId })
);
});
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



