I have a very frustrating problem (for me anyway):
I have a simple scenario where I have a Product entity and a CombinedProduct Entity. There is a many to many relationship between them, so a Product can belong to multiple CombinedProducts and a CombinedProduct can contain many products.
I first create a product and save this to the database. Later on I create a CombinedProduct and add this product. When I try to save this CombinedProduct, the product entity is added again to the database by Entity Framework instead of just adding a relationship… This is really driving me nuts.
I already tried to attach the product again to the context before saving, but Entity complains that it already has a product with the same key…
Below you find the code for all of this (simplified and code stripped):
Product Entity
Public Class SingleProduct
Property SingleProductId As Integer
Property CombinedProducts As ICollection(Of CombinedProduct)
End Class
CombinedProduct
Public Class CombinedProduct
Public Sub New()
Me.Products = New HashSet(Of SingleProduct)()
End Sub
Property CombinedProductId As Integer
Property Products As ICollection(Of SingleProduct)
End Class
Many to Many Relastionship definition
Protected Overrides Sub OnModelCreating(modelBuilder As DbModelBuilder)
modelBuilder.Entity(Of CombinedProduct)().
HasMany(Function(c) c.Products).
WithMany(Function(p) p.CombinedProducts).
Map(Sub(m)
m.ToTable("CombinedProductSingleProducts")
m.MapLeftKey("SingleProductId")
m.MapRightKey("CombinedProductId")
End Sub)
End Sub
Code used for saving
Using myDataContext As New DataContext
myDataContext.CombinedProducts.Add(product)
myDataContext.SaveChanges()
End Using
Already tried attaching before saving but that didn’t work
For Each prd In product.Products
If myDataContext.Entry(prd).State = EntityState.Detached Then
myDataContext.SingleProducts.Attach(prd)
End If
Next
One “solution” I found was right before saving, clearing the products list and getting the products again from the database and add those to the CombinedProduct, but that can hardly be the solution.
Hope someone can help me, this is driving me nuts!
(I use Entity Framework 4.1, with Code First)
Edit
Adding the product: This is done in it’s own datacontext at some other form:
Using myDataContext As New DataContext
myDataContext.SingleProducts.Add(singleProduct)
myDataContext.SaveChanges()
End Using
The combined product creation:
Dim myCombinedProduct = New CombinedProduct
myCombinedProduct.Products.Add(product)
The product I add is first fetched again in it’s own datacontext:
Using myDataContext As New DataContext
Return myDataContext.Products.FirstOrDefault(Function(p) p.ProductId = id)
End Using
Edit
The full story in the hopes of being more clear:
I have a winforms application with two forms: one for managing products and one for managing combined products. The application is N-Tier (User layer, business layer and data layer).
On the form for managing you products you can simply add/update/delete products. On this form everything works fine.
The combined product form is another matter:
- when loading the form I retrieve all products from the database, going through the business and datalayer. The function to retrieve the products in the datalayer has it’s own DataContext (using block). This function returns an iEnumerable of Products.
- The retrieved products are added to a number of comboboxes as objects.
- You select the products you want to add to the combined product by selecting them
- when saving I create a new CombinedProduct entity in the user layer, retrieve the product objects from the comboboxes and add them to the new CombinedProduct Object
- I send the CombinedProduct object to the business layer where I perform a number of business rules
- If all is well, the combinedProduct is send to the datalayer, where I try to save it again in it’s own datacontext (using-block).
So I have multiple DataContexts as they live and die in the datalayer.
Hope this makes things a bit more clear.
In EF all entities that you are trying to relate must belong to the same datacontext,other wise it would not recognize your product as an existing one, and would add a new one instead
in your first usings, your product must be fetched from db first.
I never had to attatch/detach entities, but i have seen most people run into issues doing that.
Also in many to many, both entities have to be added to the context, relationship is just going to fill the join table
looking at the discussion it looks like you have a repository for each entity type, and are getting objects through those repository methods. If thats the case, then IMO this design has a problem, since object relationships are stored in a common context.In your case all these repositories should have the same dbcontexts. You might think that going to the database has some performance issues, but you are going to the db even when you attach.