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

EF Core与.NET 8中多对多关联表插入数据失败排查

问题:创建订单时触发UserId非空约束错误且OrderDetails无数据

场景说明

在.NET 8环境下使用EF Core配置了Order、Product及多对多关联表OrderDetail模型,尝试通过API端点创建包含2个商品及对应数量的新订单时,Orders表生成了订单记录,但OrderDetails表无数据,同时出现PostgreSQL非空约束错误。

错误信息

Npgsql.PostgresException (0x80004005): 23502: null value in column "UserId" of relation "Orders" violates not-null constraint

DETAIL: Failing row contains (15, null, 1, 2024-01-01 14:07:25.091254+00, 2024-01-01 14:07:25.091254+00, null, null).

Exception data:

Severity: ERROR

SqlState: 23502

MessageText: null value in column "UserId" of relation "Orders" violates not-null constraint

Detail: Failing row contains (15, null, 1, 2024-01-01 14:07:25.091254+00, 2024-01-01 14:07:25.091254+00, null, null).

SchemaName: public

TableName: Orders

ColumnName: UserId

File: execMain.c

Line: 2003

Routine: ExecConstraints

解决方案

1. 修复OrderDetail实体配置

当前OrderDetail中导航属性Order和Product使用new()初始化,EF会自动跟踪这个新创建的空Order实例,导致保存时插入一条UserId为空的无效订单。同时需要为OrderDetail配置复合主键(因为它是多对多关联表,主键由OrderId和ProductId组成)。

修改OrderDetail类:

public class OrderDetail
{
    public int ProductId { get; set; }
    public Product Product { get; set; } = null!;
    public int OrderId { get; set; }
    public Order Order { get; set; } = null!;
    public int Amount { get; set; }
}

在DkdbContext的OnModelCreating方法中添加复合主键配置:

builder.Entity<OrderDetail>()
    .HasKey(od => new { od.OrderId, od.ProductId });

2. 优化订单创建逻辑,合并保存操作

原代码分两次调用SaveChangesAsync,且手动设置OrderId,容易引发跟踪冲突。直接通过Order的OrderDetails集合添加关联数据,一次完成保存,让EF自动处理外键关联。

修改端点代码:

public class OrderEnpoint
{
    public static void Map(WebApplication app)
    {
        app.MapPost("/order", async (DkdbContext db, OrderDto orderRequest, UserManager<DkUser> userManager) =>
        {
            if (orderRequest == null || string.IsNullOrEmpty(orderRequest.UserId))
                return Results.BadRequest();

            var user = await userManager.FindByIdAsync(orderRequest.UserId);
            if (user == null)
                return Results.NotFound("指定用户不存在");

            // 验证商品ID是否有效(可选但推荐)
            var productIds = orderRequest.ProductOrderList.Select(e => e.ProductId).ToList();
            var existingProductCount = await db.Products.CountAsync(p => productIds.Contains(p.Id));
            if (existingProductCount != productIds.Count)
                return Results.BadRequest("存在无效的商品ID");

            var newOrder = new Order
            {
                UserId = orderRequest.UserId,
                CreatedBy = user.UserName,
                UpdatedBy = user.UserName,
                // 直接将OrderDetail关联到Order的集合中
                OrderDetails = orderRequest.ProductOrderList
                    .Select(e => new OrderDetail
                    {
                        ProductId = e.ProductId,
                        Amount = e.Amount
                    }).ToList()
            };

            await db.Orders.AddAsync(newOrder);
            await db.SaveChangesAsync();

            return Results.Created($"/order/{newOrder.Id}", new { OrderId = newOrder.Id });
        });
    }
}

3. 补充数据验证(可选)

为避免无效数据插入,可在创建订单前验证:

  • 用户ID对应的用户是否存在
  • 商品ID是否存在于Products表中
  • 商品数量Amount是否大于0

原因分析

  1. 无效Order实例跟踪:OrderDetail中Order = new()会创建一个空的Order对象,EF上下文会自动跟踪该实例,保存时尝试插入这条UserId为空的记录,触发非空约束错误。
  2. 多次保存的冲突:原代码分两次保存,第一次保存有效订单后,第二次添加OrderDetail时,EF对导航属性的跟踪可能出现冲突,导致OrderDetails数据未正确插入。

内容的提问来源于stack exchange,提问作者Vitou Hun

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 05:17:33