首页 > 解决方案 > 如何一次添加多个实体和孩子

问题描述

我在 foreach 循环中一次添加具有多个子项的多个实体时遇到问题。
其明显的 ef 无法在 foreach 循环中跟踪 id。但也许还有其他解决方案可以指导我。

当我尝试将多个实体与孩子一起添加时的错误是:

无法跟踪实体类型的实例,因为已经在跟踪具有与 {'Id'} 相同键值的另一个实例。附加现有实体时,请确保仅附加一个具有给定键值的实体实例。

例如:

public class Order
{
    public Order()
    {
        OrderDetails = new HashSet<OrderDetail>();
    }

    [Key]
    public int Id { get; set; }

    [StringLength(50)]
    public string Code { get; set; }

    public int? CompanyId { get; set; }
    public int? PartnerId { get; set; }
    public decimal TotalNetPrice { get; set; }
    public decimal TotalPrice { get; set; } 
    public bool IsActive { get; set; } = true;
    public bool IsDeleted { get; set; } = false;

    [ForeignKey("CompanyId")]
    public virtual Company Company { get; set; }

    [ForeignKey("PartnerId")]
    public virtual Partner Partner { get; set; }

    public virtual ICollection<OrderDetail> OrderDetails { get; set; }
}

public class OrderDetail
{

    [Key]
    public int Id { get; set; }

    public int OrderId { get; set; }

    [StringLength(50)]
    public string Code { get; set; }

    public int LineNumber { get; set; }
    public int ProductId { get; set; }
    public int Quantity { get; set; }
    public bool IsActive { get; set; } = true;
    public bool IsDeleted { get; set; } = false;

    [ForeignKey("OrderId")]
    public virtual Order Order { get; set; }

    [ForeignKey("ProductId")]
    public virtual Product Product { get; set; }
}

这是我在方法中的代码:

foreach (var order in orderList)
{
    // consider we create/cast object to Order class.
    _orderService.Add(order);
    // in here we don't have id, because we didn't call savechanges.

    foreach(var orderDetail in order.orderDetailList)
    {
        // consider we create/cast object to OrderDetail class.
        orderDetail.orderId = order.Id;
        // in here, order id is always 0 as expected.
        _order.OrderDetails.Add(orderDetail);
    }
}

try 
{
    await uow.SaveChangesAsync();
}
catch(Exception exception)
{
    var msg = exception.Message;
}

我尝试对[DatabaseGenerated(DatabaseGeneratedOption.Identity)]标识列使用属性。

根据文档(https://docs.microsoft.com/en-us/ef/core/modeling/generated-properties?tabs=data-annotations):

根据所使用的数据库提供程序,值可能由 EF 或在数据库中生成客户端。如果该值是由数据库生成的,那么 EF 可能会在您将实体添加到上下文时分配一个临时值。然后,此临时值将被 SaveChanges() 期间数据库生成的值替换。

所以它应该至少提供临时 ID 来跟踪它。但它不适用于我的情况。
我还尝试了相同的方法来创建上下文中的模型。但同样的结果。没有成功。

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Entity>()
        .Property(x => x.Id)
        .ValueGeneratedOnAdd();
}

似乎最好的解决方案是在详细信息之前进行交易并保存订单并获取真实ID,然后添加详细信息。但如您所知,它存在性能问题。
我想知道这个问题是否还有其他最佳实践?
谢谢你。

标签: c#entity-frameworkasp.net-coreentity-framework-core

解决方案


尝试这个:

foreach (var order in orderList)
{
    _orderService.Add(order);

    foreach(var orderDetail in order.orderDetailList)
    {
        // Add reference Of Order to OrderDetails, not an id
        orderDetail.Order = order;
        _order.OrderDetails.Add(orderDetail);
    }
}

在这种情况下,EF 将知道如何在 SaveChangesAsync 上连接 Order 和 OrderDetail


推荐阅读