实体框架在重新获取后复制上下文中的对象

时间:2017-10-17 15:20:28

标签: asp.net-core entity-framework-6

我注意到实体框架使用.NetCore 1.1和EF 6.1.3在我的内存上下文中复制子对象。有人可以解释这种行为吗?

例如,假设我有以下数据模型:

public class Customer 
{
    public string Name { get; set; }
    public string SomeDataINeed { get; set; }
    public int CustomerId { get; set; }
    public virtual List<Order> Orders { get; set; }
}

public class Order
{
    public string Description { get; set; }
    public double BundledDiscount {get; set; }
    public int OrderId { get; set; }
    public int CustomerId { get; set; }
    public virtual Customer Cust { get; set; }
    public virtual List<LineItem> LineItems { get; set; }
}

public class LineItem
{
    public int OrderId { get; set; }
    public int LineItemId { get; set; }
    public double LineCost { get; set; }
    public virtual Order ParentOrder { get; set; }
}

然后我有一个API端点,我在其中放置一个Order(其LineItems也在put的主体中)然后从数据库中取出Customer。我必须获取完整的客户,因为我需要用一些非持久性数据来装饰它我从另一个API中获取。

当我获取Customer时,LineItems在上下文中重复。例如,如果我在原始放置中有两个LineItem,我现在将有4个,具有重复的主键。

[HttpPut]
public async Task<IActionResult> PutOrder([FromBody] Order order)
{
    var customerId = order.CustomerId

    _context.Entry(order).State = EntityState.Modified;
    _context.SaveChanges();

    // now I need to fetch the full customer (just trust me, I need to fetch it).  
    // After the below call, any Line Items get duplicated inside the context, 
    // even though they are set up with primary keys.  
    // For some reason, EF does not use the primary keys to know that some 
    // line items were already in memory.
    var customer = _context.Customer
        .Include(x => x.Orders)
        .ThenInclude(y => y.LineItems)
        .Where(c => c.CustomerId == customerId)
        .FirstOrDefault()

    return Ok(customer);
}

到目前为止,我发现的唯一解决方案是首先将原始对象从上下文中分离出来......但似乎我不应该这样做。似乎Entity Framework应该能够根据主键和外键自动删除重复。

这是另一个发生了什么的例子。首次EF操作后的上下文:

context: {
   Order: {
       id: 1,
       LineItems: [{id: 33}] // I'm not trying to affect the state here.  LineItems are only here because they are in the body of the put
   }
}

第二次EF操作后的上下文:

context: {
    Customer: {
        Order: {
            id: 1,
            LineItems: [
                {id: 33},
                {id: 33} // The line item is duplicated here
                ] 
        }
    }
}

谢谢!

1 个答案:

答案 0 :(得分:1)

它正在复制它们,因为正如@Steve Greene所说,当您设置父实体的状态时,子实体不会被修改。就上下文而言,PUT中的这些订单项是新实体,尽管它们具有主键。

您必须浏览每个子项并将其附加到上下文中。通过这样做,如果他们有主键,则应将其设置为Unchanged状态;如果他们没有主键,则应将其设置为Added