EF 5.0 - 使用Code First无法建立双向关系

3
我有一个名为WidgetCollection的类。它具有公开List(Of Widget)类型的Items属性和SelectedWidget属性。我希望EF按照以下方式构建数据库:
  • 在我的Widgets表中添加一个WidgetCollection_Id属性,以指定每个widget所在的WidgetCollection
  • 在我的WidgetCollection表中添加一个SelectedWidget_Id属性,以指定所选Widget是哪一个
  • 从WidgetCollection.Id到Widget.WidgetCollection_Id添加一对多的关系
  • 从Widget.Id到WidgetCollection.SelectedWidget_Id添加一个1对0或1的关系
可以确认它似乎正确地构建了数据库模式,但如果赋值给SelectedWidget后保存上下文,就会得到以下错误:

System.Data.Entity.Infrastructure.DbUpdateException occurred HResult=-2146233087 Message=An error occurred while saving entities that do not expose foreign key > properties for their relationships. The EntityEntries property will return null because a single entity cannot be identified as the source of the exception. Handling of exceptions while saving can be made easier by exposing foreign key properties in your entity types.

并且内部异常为:

Unable to determine a valid ordering for dependent operations. Dependencies may exist due to foreign key constraints, model requirements, or store-generated values.

如果不将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个回答

2
我认为异常信息的意思就是它所说的:
无法确定依赖操作的有效顺序。
这两行代码...
oWidgetCollection.Widgets.Add(oWidgetA)
oWidgetCollection.SelectedWidget = oWidgetA 

这段文字涉及到IT技术,意思是EF在设置oWidgetA的外键WidgetCollection_Id之前必须先存储oWidgetCollection。而第二行要求以相反的方式存储对象,即必须先存储oWidgetA才能让EF设置oWidgetCollection中的外键SelectedWidget_Id
为了解决这个冲突,我认为您需要进行两次保存更改。
oWidgetCollection.Widgets.Add(oWidgetA)
oWidgetCollection.Widgets.Add(oWidgetB)

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

oWidgetCollection.SelectedWidget = oWidgetA
oContext.SaveChanges()

顺便提一下:这个期望不正确。从Widget.Id到WidgetCollection.SelectedWidget_Id添加一个1对0或1的关系是错误的。EF将创建另一个一对多的关系,即相同的SelectedWidget可以被选择为许多WidgetCollections。当您在关系的一侧只有导航属性时,默认关系EF将按惯例创建始终为一对多。您需要使用数据注释或Fluent API来覆盖此默认行为。
我建议将此关系保留为一对多。一对一关系更加困难,而且EF仅支持具有共享主键的一对一关系,这意味着您无法选择不同的小部件作为所选。唯一可能的选定小部件将是具有与WidgetCollection相同的主键值的那个。

虽然我在原始项目中尝试过这个方法,但我没有在示例项目中尝试过,因此错误地假设我复制的错误具有相同的原因。事实证明,我的原始项目中真正的问题在于从我的集合ID获取器中获取属性。 是一个愚蠢的问题,但也是一个违反直觉的错误消息。感谢您给予正确方向的推动! - Hamish

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接