I am stuck at the operation when using Entity Framework Core 2 to perform an insert of a new entity with one related entity which already existed in the database. For example, I have two objects with one to many relationship:
public Class OrderDetail
{
public int ParentID {get;set;}
public string OrderDetailName {get;set;}
public int ProductID {get;set;}
public virtual Product ProductFK {get;set;}
}
public Class Product
{
public int ProductID {get;set;}
public string ProductName {get;set;}
public virtual Collection<OrderDetail> OrderDetails {get;set;} = new Collection<OrderDetail>();
}
I would like to add a new OrderDetail
with an existing Product
(with productID = 3
) into the database, so I perform like:
private void AddOrderDetail(int productID)
{
OrderDetail newOrderDetail = new OrderDetail();
newOrderDetail.Name = "New OrderDetail";
// Here I have no clue what would be the correct way...Should I do
// Approach A(pick related entity from database then assign as related entity to base entity):
var ProductFromDB = DbContext.Products.SingleOrDefault(p => p.ProductID == productID);
newOrderDetail.ProductFK = ProductFromDB;
// Approach B(directly assign the foreign key value to base entity)
newOrderDetail.ProductID = productID
DbContext.SaveChange();
}
By using approach (A), my newOrderDetail
failed to save and I looked into SQL resource, looks like it considered the one that I retrieved from the database (ProductFromDB
) as a new object and was trying to insert it again. I feel it's redundant job for picking ProductFromDB
first then assign to the inserted entity...
By using approach (B), my newOrderDetail
still failed to save, and I am getting an exception like "insert A second operation started on this context before a previous operation completed. Any instance members are not guaranteed to be thread safe.", however, this exception does not happen constantly. I looked into the SQL and found the SQL Script, by running it individually in SQL Server it worked, however when running an application side, it's not working...
So what would be the correct way to deal with above scenario?
If you don't need access to the complete Product
object right away, you could try to set just the foreign key column value of your newOrderDetail
. Also, you need to ADD your newly created OrderDetail
object to the DbContext
before saving - something like this:
private void AddOrderDetail(int productID)
{
OrderDetail newOrderDetail = new OrderDetail();
newOrderDetail.Name = "New OrderDetail";
newOrderDetail.ProductID = productID
// OF COURSE, you need to ADD the newly created object to the context!
DbContext.OrderDetails.Add(newOrderDetail);
DbContext.SaveChange();
}
Next time around, when you actually fetch the OrderDetail
, it will resolve the linked product and you should be fine.:
using (var DbContext = new YourDbContext())
{
OrderDetail od = DbContext.OrderDetails.FirstOrDefault(x => x.Name = "New OrderDetail");
....
}