You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 01:45:02