I’m playing with Entity Framework and hit a stumbling block regarding many-to-many mappings.
Lets say I have two entities, Orders and Products.
Inside my Order entity I have:
int OrderID,
double OrderPrice
datetime OrderDate
ICollection<Product> Products
datetime DispactDate
and so on…
then inside my product class I have
int ProductID
string ProductName
string ProductDescription
and so on.
In my Code First EF DB I have a Order Table which maps nicely to my order, a products table with a list of products that can be ordered and then using EF Code-first migrations EF created OrderProducts with foreign keys to both primary keys of the other tables. All good.
However, when I go to save an order which has multiple products in its Product Collection, EF is duplicating all the entries in the Product table. So If I add products 10, 12 & 13, its crating three new rows with three new ID’s and mapping to them, rather than the originals. If I then process another order with another 20 existing products, 20 new entries are made in the products table!
In my DBContext I have overridden the onModelCreatingEvent:
modelBuilder.Entity<Order>()
.HasMany(x=>x.Products)
.WithMany()
.Map(be =>
{
be.ToTable("OrderProducts");
});
I’m guessing something is configured wrong here. I got this from How to map many-to-many relationships in Entity Framework CTP5?
Previously I had two separate repositories – one for orders and one for products. I’ve now merged these into orderRepository which exposes both Orders and Products so that I can attach a product to the orderRepository context.
To generate the products, I have a Controller action AddProducts:
public RedirectToRouteResult Products(FormCollection selectedProducts)
{
currentOrder.Products = new List<Product>();
foreach (string thisProduct in selectedProducts)
{
int thisProductID = int.Parse(thisProduct);
Product selectedProduct = orderRepository.Products.Where(e => e.ProductID == thisProductID).FirstOrDefault();
if (selectedProduct != null)
{
orderRepository.AttachToContext(selectedProduct);
currentOrder.Products.Add(selectedProduct);
}
}
orderRepository.saveChanges(currentOrder);
}
Can you knock up a little unit test that just creates the context, retrieves a product, creates an order and adds the retrieved product to the order?
That should work absolutely fine. It sounds like you are creating a new product object rather than using a pre-existing one. Or creating one and not setting the appropriate entity key.
It doesn’t sound like you are getting it from a different context as you would usually get a warning that it is already attached to another context.