Ensure that the OrderDetails are associated with the context that the order is being created. The typical cause is the order details are loaded by a different context, perhaps de-serialized from a web request and associated to the new order. The problem is that the context instance saving the orders does not recognize those OrderDetails and treats them as new (added) entities.
Given an example method like this:
public Order CreateOrder(IEnumerable<OrderDetail> orderDetails)
{
using (var context = new AppContext())
{
var order = new Order();
order.OrderDetails.AddRange(orderDetails);
context.Orders.Add(order);
context.SaveChanges();
return order;
}
}
The error occurs because context above does not know about the OrderDetails. Adding the objects to the new order are treated like adding new OrderDetails leading to PK violations or inserting duplicates with new IDs if PKs are generated.
Option 1: When creating a new entity, be sure to load all associated entities
Rather than passing entities for associations, you can save bandwidth by passing IDs:
public Order CreateOrder(IEnumerable<int> orderDetailIds)
{
using (var context = new AppContext())
{
var order = new Order();
var orderDetails = context.OrderDetails.Where(x => orderDetailIds.Contains(x.OrderDetailId)).ToList();
order.OrderDetails.AddRange(orderDetails);
context.Orders.Add(order);
context.SaveChanges();
return order;
}
}
This ensures the order details are known by the context, and associated to the new order rather than treated as new records.
Option 2: Associate entities to the context
public Order CreateOrder(IEnumerable<OrderDetail> orderDetails)
{
using (var context = new AppContext())
{
var order = new Order();
foreach(var orderDetail in orderDetails)
{
context.OrderDetails.Attach(orderDetail);
}
order.OrderDetails.AddRange(orderDetails);
context.Orders.Add(order);
context.SaveChanges();
return order;
}
}
This option associates the order details with the context by attaching them. This can work ok when the DbContext is scoped to the method, and assuming all references being attached are unique (no double-ups). For longer-lived calls you need to be careful if there is a possibility of an entity already having been attached or otherwise loaded by the context where the Attach call can fail. Attaching entities coming from a web client should also not be trusted, so you should ensure that the entity state never gets set to modified or this opens the door for tampered data being persisted with a SaveChanges call.
There are hybrid options that can check the local cache for references and create stubs which I was about to outline but these are really only suited to bulk operations and come with significant risks.