EF Core:如何将Owned Collection配置为数据库非空项?
问题:如何通过EF Core Fluent API将JSON存储的集合列配置为非空?
我定义了以下实体:
public class Cart { public required Guid Id { get; init; } public required List<CartProduct> Products { get; init; } } public class CartProduct { public required Guid ProductId { get; init; } public required int Quantity { get; set; } }
并通过如下代码配置Cart实体:
public class CartEntityTypeConfiguration : IEntityTypeConfiguration<Cart> { public void Configure(EntityTypeBuilder<Cart> cartEntity) { cartEntity.ToTable("carts"); cartEntity .HasKey(cart => cart.Id) .HasName("pk_carts"); cartEntity .Property(cart => cart.Id) .HasColumnName("id"); cartEntity.OwnsMany( cart => cart.Products, products => products.ToJson("products") ); } }
但生成的迁移中,products对应的JSON列被标记为可空:
migrationBuilder.CreateTable( name: "carts", columns: table => new { id = table.Column<Guid>(type: "uuid", nullable: false), products = table.Column<string>(type: "jsonb", nullable: true) }, constraints: table => { table.PrimaryKey("pk_carts", x => x.id); } );
我尝试了两种方式配置该属性为必填,都触发了相同错误:
第一种配置:
cartEntity.OwnsMany( cart => cart.Products, products => products.ToJson("products") ); cartEntity .Navigation(cart => cart.Products) .IsRequired();
第二种配置:
cartEntity .OwnsMany( cart => cart.Products, products => products.ToJson("products") ) .Navigation(cart => cart.Products) .IsRequired();
错误提示:
Unable to create a 'DbContext' of type ''. The exception ''Cart.Products' cannot be configured as required since it was configured as a collection.' was thrown while attempting to create an instance.
请问有没有办法通过Fluent API直接将该列配置为非空?(不想手动修改迁移文件)
解决方案
EF Core里,集合导航属性不能直接用Navigation.IsRequired()标记为必填,但对于用ToJson映射的JSON列,可以通过配置对应的阴影属性来设置列非空:
方法一:在OwnsMany配置中直接设置JSON列属性
public class CartEntityTypeConfiguration : IEntityTypeConfiguration<Cart> { public void Configure(EntityTypeBuilder<Cart> cartEntity) { cartEntity.ToTable("carts"); cartEntity .HasKey(cart => cart.Id) .HasName("pk_carts"); cartEntity .Property(cart => cart.Id) .HasColumnName("id"); cartEntity.OwnsMany( cart => cart.Products, products => { products.ToJson("products"); // 配置JSON列非空 cartEntity.Property("products").IsRequired(); } ); } }
方法二:单独配置JSON列
public class CartEntityTypeConfiguration : IEntityTypeConfiguration<Cart> { public void Configure(EntityTypeBuilder<Cart> cartEntity) { cartEntity.ToTable("carts"); cartEntity .HasKey(cart => cart.Id) .HasName("pk_carts"); cartEntity .Property(cart => cart.Id) .HasColumnName("id"); // 先配置集合为JSON列 cartEntity.OwnsMany(cart => cart.Products, products => products.ToJson("products")); // 单独设置JSON列非空 cartEntity.Property("products").IsRequired(); } }
这样生成的迁移中,products列就会被标记为nullable: false:
migrationBuilder.CreateTable( name: "carts", columns: table => new { id = table.Column<Guid>(type: "uuid", nullable: false), products = table.Column<string>(type: "jsonb", nullable: false) }, constraints: table => { table.PrimaryKey("pk_carts", x => x.id); } );
原理
当用ToJson将集合映射为JSON列时,EF Core会在主实体上创建一个名为products的阴影属性(没有对应实体类的CLR属性)。直接配置这个阴影属性的IsRequired(),就能修改数据库列的可空性,同时避开集合导航不能标记为必填的限制。
内容的提问来源于stack exchange,提问作者satma0745
相关产品推荐
相关产品推荐

