实体框架4.1:保存多对多关系在db中再次保存现有子实体

时间:2011-11-20 21:12:00

标签: entity-framework entity-framework-4.1

我有一个非常令人沮丧的问题(无论如何对我来说):

我有一个简单的场景,我有一个Product实体和一个CombinedProduct实体。它们之间存在多对多的关系,因此产品可以属于多个CombinedProducts,而CombinedProduct可以包含许多产品。

我首先创建一个产品并将其保存到数据库中。稍后我创建了一个CombinedProduct并添加了这个产品。当我尝试保存此CombinedProduct时,产品实体将再次通过Entity Framework添加到数据库中,而不仅仅是添加关系...这真的让我疯狂。

我已经尝试在保存之前将产品再次附加到上下文中,但实体抱怨它已经有一个具有相同密钥的产品...

下面你会找到所有这些的代码(简化和代码剥离):

产品实体

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

多对多的Relastionship定义

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

用于保存的代码

Using myDataContext As New DataContext
  myDataContext.CombinedProducts.Add(product)
  myDataContext.SaveChanges()
End Using

在保存之前已经尝试过附加但是没有效果

For Each prd In product.Products
   If myDataContext.Entry(prd).State = EntityState.Detached Then
      myDataContext.SingleProducts.Attach(prd)
   End If
Next

我找到的一个“解决方案”是在保存之前,清除产品列表并从数据库中再次获取产品并将其添加到CombinedProduct,但这几乎不是解决方案。

希望有人可以帮助我,这让我疯了! (我使用实体框架4.1,代码优先)

修改

添加产品:这是通过其自己的datacontext以其他形式完成的:

Using myDataContext As New DataContext
    myDataContext.SingleProducts.Add(singleProduct)
    myDataContext.SaveChanges()
End Using

组合产品创建

Dim myCombinedProduct = New CombinedProduct
myCombinedProduct.Products.Add(product)

我添加的产品首先在它自己的datacontext中再次获取:

Using myDataContext As New DataContext
    Return myDataContext.Products.FirstOrDefault(Function(p) p.ProductId = id)
End Using

修改

希望更清楚的完整故事:

我有一个winforms应用程序有两种形式:一种用于管理产品,另一种用于管理组合产品。该应用程序是N层(用户层,业务层和数据层)。

在用于管理产品的表单上,您只需添加/更新/删除产品即可。在这个表格上一切正常。

组合产品形式是另一回事:

  1. 加载表单时,我从数据库中检索所有产品,通过业务和数据层。在数据层中检索产品的功能有自己的DataContext(使用块)。此函数返回iEnumerable of Products。
  2. 将检索到的产品作为对象添加到多个组合框中。
  3. 您可以选择要添加到组合产品中的产品
  4. 保存时我在用户层创建一个新的CombinedProduct实体,从组合框中检索产品对象并将它们添加到新的CombinedProduct对象
  5. 我将CombinedProduct对象发送到我执行许多业务规则的业务层
  6. 如果一切正常,则将combinedProduct发送到数据层,我尝试将其再次保存在自己的datacontext中(使用块)。
  7. 所以我有多个DataContexts,因为它们存在并死在数据层中。

    希望这会让事情变得更加清晰。

2 个答案:

答案 0 :(得分:1)

我不完全明白你在哪个步骤中执行哪个步骤但正确的顺序应该是:

如果一切都在一个环境中发生:

Using myDataContext As New DataContext
    Dim product = myDataContext.Products.FirstOrDefault(Function(p) p.ProductId = id)

    Dim myCombinedProduct = New CombinedProduct
    myCombinedProduct.Products.Add(product)

    myDataContext.CombinedProducts.Add(myCombinedProduct)
End Using

加载product附加到上下文并避免重复。必须在之前myCombinedProduct添加到上下文中。

如果您在另一个上下文中加载product

Using myDataContext As New DataContext
    myDataContext.Products.Attach(product)

    Dim myCombinedProduct = New CombinedProduct
    myCombinedProduct.Products.Add(product)

    myDataContext.CombinedProducts.Add(myCombinedProduct)
End Using

您必须先将product附加到新的上下文,然后再将附加到,然后将myCombinedProduct添加到上下文中。

修改

如果您的新myCombinedProduct 包含产品集进入分离状态的数据层,则以下内容应该有效:

Using myDataContext As New DataContext
    For Each prd In myCombinedProduct.Products
        myDataContext.SingleProducts.Attach(prd)
    Next

    myDataContext.CombinedProducts.Add(myCombinedProduct)
End Using

答案 1 :(得分:0)

在EF中,您尝试关联的所有实体必须属于同一个datacontext,否则它不会将您的产品识别为现有产品,而是会添加新产品 在您的第一次使用中,您的产品必须首先从db获取。

我从来没有必须附加/分离实体,但我看到大多数人都遇到了这样的问题。

同样在很多人中,两个实体都必须添加到上下文中,关系只是填充连接表

查看讨论,看起来您拥有每个实体类型的存储库,并通过这些存储库方法获取对象。如果是这种情况,那么IMO这个设计有一个问题,因为对象关系存储在一个公共的上下文中。在你的情况下,所有这些存储库应该具有相同的dbcontexts。您可能认为进入数据库会出现一些性能问题,但即使您附加了数据库,也会进入数据库。