I am receiving the following error when trying to insert an object into a child collection after the model binder has created the model, children and grandchildren and then using context.SaveChanges();
Multiplicity constraint violated. The role 'OrderDetail_OrderDetailPricelistProductOptions_Source' of the relationship 'PPLib.Models.OrderDetail_OrderDetailPricelistProductOptions' has multiplicity 1 or 0..1.
My models are as follows (removed properties for brevity);
public class Order
{
public int OrderId { get; set; }
public virtual List<OrderDetail> OrderDetails { get; set; }
}
public class OrderDetail
{
public int OrderDetailId { get; set; }
public int OrderId { get; set; }
public int ProductId { get; set; }
public virtual Product Product { get; set; } //FK NAV
public int? PricelistProductId { get; set; } // if a subscriber order ...has the ProductId from a PriceList.
private decimal _Price = 0;
public decimal Price { get { return _Price; } set { _Price = value; } }
private int _Quantity = 1;
public int Quantity { get { return _Quantity; } set { _Quantity = value; } }
public virtual List<OrderDetailPricelistProductOption> OrderDetailPricelistProductOptions { get; set; }
}
public class OrderDetailPricelistProductOption
{
public int OrderDetailPricelistProductOptionId { get; set; }
public int OrderDetailId { get; set; }
public virtual List<OrderDetailPricelistProductOptionsDetail> OrderDetailPricelistProductOptionsDetails { get; set; }
}
public class OrderDetailPricelistProductOptionsDetail
{
public int OrderDetailPricelistProductOptionsDetailId { get; set; }
public int OrderDetailPricelistProductOptionId { get; set; }
public string Name { get; set; }
}
To be clearer:
If I submit a complete new Order, with a list of OrderDetails, its list of OrderDetailPricelistProductOptions and its list of OrderDetailPricelistProductOptionsDetails, the model binder does its job and I receive no error doing:
db.Orders.Add(order);
db.SaveChanges();
If I submit an Edit with and Existing Order and a NEW a list of OrderDetails, its list of OrderDetailPricelistProductOptions and its list of OrderDetailPricelistProductOptionsDetails, I get the Order from the DB context and then merge the OrderDetails from the view model, using:
order.OrderDetails.AddRange(pricelistProductVM.Order.OrderDetails);
and I receive no error doing:
db.Entry(order).State = EntityState.Modified;
db.SaveChanges();
I have a particular situation, where I have to instantiate a new OrderDetail called autoFillOd, and inject its values from one of the existing OrderDetails assembled by the Model Binder. I change its Quantity value and then add it to the collection of OrderDetails in the ViewModel, like so:
pricelistProductVM.Order.OrderDetails.Add(autoFillOd);
When I do db.SaveChanges(), I receive the error.
You'll notice that the error is on the child of the OrderDetails: OrderDetail_OrderDetailPricelistProductOptions_Source
Why can I not add an OrderDetail dynamically into the collection of OrderDetails? All the OrderDetails are new (to be inserted) so the values are the same between the copies, except for the Quantity property which should not be an issue.
The controller action is as follows:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Add(pricelistProductVM pricelistProductVM)
{
OrderLogic ol = new OrderLogic();
//Auth is running on execute
int userId = WebSecurity.CurrentUserId;
int websiteId = (int)Session["websiteId"];
int? id = null; // mediaId
int productId = pricelistProductVM.Product.ProductId;
int? eventId = pricelistProductVM.eventId;
string err = "";
if (productId > 0)
{
//Set Pricelist
Pricelist pricelist = ol.setPricelist(websiteId, id, eventId);
if (pricelist.PricelistId != 0)
{
//get the pricelistproduct from the pricelist
PricelistProduct pp = await (from ppx in db.PricelistProducts
where ppx.ProductId == productId
&& ppx.PricelistId == pricelist.PricelistId
&& ppx.isAvailable == true
&& ppx.DiscontinuedDate == null
&& ppx.Product.isAvailable == true
&& ppx.Product.DiscontinuedDate == null
select ppx).SingleOrDefaultAsync();
if (pp != null)
{
Order order = new Order();
//set some default values for the Order entity
if (pricelistProductVM.Order.OrderId == 0)
{
pricelistProductVM.Order.WebsiteId = websiteId;
pricelistProductVM.Order.UserId = userId;
pricelistProductVM.Order.EventId = eventId;
pricelistProductVM.Order.StartedDate = DateTime.UtcNow;
order = pricelistProductVM.Order;
}
else
{
order = await db.Orders.FindAsync(pricelistProductVM.Order.OrderId);
}
//set some default values for the OrderDetails entity
pricelistProductVM.Order.OrderDetails.First().InjectFrom(pp);
pricelistProductVM.Order.OrderDetails.First().IsPackage = false;
//determine if this product should be automatically added to any packages in the order
OrderDetail autoFillOd = ol.packageCheck(ref pp, ref pricelistProductVM, ref order, websiteId, db);
if (autoFillOd != null)
{
if (autoFillOd.Quantity > 0)
{
//This is where the OrderDetail that causes a problem is added
pricelistProductVM.Order.OrderDetails.Add(autoFillOd);
}
}
if (pricelistProductVM.Order.OrderId == 0)
{
db.Orders.Add(order);
}
else
{
order.OrderDetails.AddRange(pricelistProductVM.Order.OrderDetails);
db.Entry(order).State = EntityState.Modified;
}
db.SaveChanges();
}
else
{
//return error
err = "The product was not found in the available pricelist. Please reload your browser and make sure you are signed-in.";
}
}
}
else
{
//return error
err = "A productId was not passed so no product could not be found. Please reload your browser and make sure you are signed-in.";
}
if (err == "")
{
ViewBag.data = JsonConvert.SerializeObject(new { Success = 1, Msg = "The product was successfully added to your cart." });
}
else
{
ViewBag.data = JsonConvert.SerializeObject(new { Success = 0, Msg = err });
}
return View();
}
I appreciate the help!
I think OrderDetailPricelistProductOption.OrderDetailId can't be single -> it should be a list because it can appear in many OrderDetails...