I have noticed entity framework duplicating child objects in my in-memory context, using .NetCore 1.1 and EF 6.1.3. Can someone explain this behavior?
For example, say I have the following data model:
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; }
}
And then I have had an API endpoint where I put an Order (with its LineItems also in the body of the put) and then fetch the Customer out of the database. I have to fetch the full customer because I need to decorate it with some non-persistent data I'm grabbing from another API.
When I fetch the Customer, the LineItems are duplicated inside the context. E.g., if I had two LineItems in the original put, I will now have 4, with duplicate primary keys.
[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);
}
The only solution I have found so far is to detach the original object from the context first...but it seems like I should not have to do this. It seems like Entity Framework should be able to de-duplicate automatically based on Primary and Foreign Keys.
Here's another illustration of what's happening. Context after first EF operation:
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
}
}
Context after the 2nd EF operation:
context: {
Customer: {
Order: {
id: 1,
LineItems: [
{id: 33},
{id: 33} // The line item is duplicated here
]
}
}
}
Thanks!