EF 5.0 - 不能使双向关系与Code First一起使用

时间:2012-10-25 18:56:58

标签: vb.net entity-framework ef-code-first entity-framework-5

我有一个名为WidgetCollection的类。它有一个Items属性,公开List(Of Widget)和SelectedWidget属性。我希望EF能够按如下方式构建数据库:

  • 在我的Widgets表中添加WidgetCollection_Id属性,指定 哪个WidgetCollection每个小部件都在
  • 在我的WidgetCollection表中添加SelectedWidget_Id属性,指定其中的哪一个 小部件已选中
  • 从WidgetCollection.Id添加1对多关系到Widget.WidgetCollection_Id
  • 从Widget.Id添加1对0或1关系到WidgetCollection.SelectedWidget_Id

我可以确认它看起来确实正确构建了数据库模式,但如果我在分配给SelectedWidget后保存了上下文,则会出现以下错误:

  

发生了System.Data.Entity.Infrastructure.DbUpdateException     的HResult = -2146233087     消息=保存未公开外键的实体时发生错误>他们关系的属性。 EntityEntries属性将返回null,因为无法将单个实体标识为异常源。通过在实体类型中公开外键属性,可以更轻松地在保存时处理异常。

内部例外

  

无法确定相关操作的有效排序。由于外键约束,模型要求或存储生成的值,可能存在依赖关系。

我可以通过永不分配WidgetCollect.SelectedWidget来阻止此错误。

我想问题是EF无法弄清楚如何处理两个方向的关系,但我找不到一种方法将它指向正确的方向。示例代码如下,欢迎所有建议!

Public Class Widget
    Private miId As Integer
    Public Property Id As Integer
        Get
            Return miId
        End Get
        Set(value As Integer)
            miId = value
        End Set
    End Property

    Private msName As String
    Public Property Name As String
        Get
            Return msName
        End Get
        Set(value As String)
            msName = value
        End Set
    End Property
End Class
Public Class WidgetCollection
    Private miId As Integer
    Public Property Id As Integer
        Get
            Return miId
        End Get
        Set(value As Integer)
            miId = value
        End Set
    End Property


    Private msName As String
    Public Property Name As String
        Get
            Return msName
        End Get
        Set(value As String)
            msName = value
        End Set
    End Property

    Private moSelectedWidget
    Public Property SelectedWidget As Widget
        Get
            Return moSelectedWidget
        End Get
        Set(value As Widget)
            moSelectedWidget = value
        End Set
    End Property

    Private moWidgets As New List(Of Widget)
    Public Property Widgets As List(Of Widget)
        Get
            Return moWidgets
        End Get
        Set(value As List(Of Widget))
            moWidgets = value
        End Set
    End Property

End Class

Public Class MyContext
    Inherits DbContext
    Public Property Widgets As DbSet(Of Widget)
    Public Property WidgetCollections As DbSet(Of WidgetCollection)
End Class

Class Application
    Public Sub New()
        Database.DefaultConnectionFactory = New SqlCeConnectionFactory("System.Data.SqlServerCe.4.0", "", "Data Source=\EFtest.sdf")
        Database.SetInitializer(New DropCreateDatabaseIfModelChanges(Of MyContext))
        Dim oContext = New MyContext

        Dim oWidgetA = New Widget With {.Name = "Widget A"}
        Dim oWidgetB = New Widget With {.Name = "Widget A"}
        Dim oWidgetCollection = New WidgetCollection With {.Name = "My widget collection"}
        oWidgetCollection.Widgets.Add(oWidgetA)
        oWidgetCollection.Widgets.Add(oWidgetB)
        oWidgetCollection.SelectedWidget = oWidgetA  'Removing this line prevents error

        oContext.WidgetCollections.Add(oWidgetCollection)
        oContext.SaveChanges()
    End Sub
End Class

1 个答案:

答案 0 :(得分:2)

我认为异常意味着它所说的内容:

  

无法确定相关操作的有效排序。

这两行...

oWidgetCollection.Widgets.Add(oWidgetA)
oWidgetCollection.SelectedWidget = oWidgetA 

...意味着EF必须先存储oWidgetCollection才能在WidgetCollection_Id中设置oWidgetA外键,但第二行需要以相反的方式存储对象,即,在EF可以在oWidgetA中设置外键SelectedWidget_Id之前必须存储oWidgetCollection

要解决冲突,我认为您必须将更改保存两次:

oWidgetCollection.Widgets.Add(oWidgetA)
oWidgetCollection.Widgets.Add(oWidgetB)

oContext.WidgetCollections.Add(oWidgetCollection)
oContext.SaveChanges()

oWidgetCollection.SelectedWidget = oWidgetA
oContext.SaveChanges()

顺便说一句:这个期望......

  

从Widget.Id添加1对0或1关系到WidgetCollection.SelectedWidget_Id

......不正确。 EF将创建另一个一对多关系,即可以为许多SelectedWidget选择相同的WidgetCollection。当您仅在关系的一侧具有导航属性时,默认关系EF将按惯例创建,始终为一对多。您需要数据注释或Fluent API来覆盖此默认行为。

我建议将这种关系保持为一对多。一对一关系更加困难,EF仅支持与共享主键的一对一关系,这意味着您无法选择不同的小部件。唯一可能选择的小部件将是具有与WidgetCollection相同的主键值的小部件。