3

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

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

我可以确认它似乎确实正确地构建了数据库模式,但是如果我在分配给 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
4

1 回答 1

2

我认为例外意味着它所说的:

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

这两行...

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

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

要解决冲突,我相信您必须保存更改两次:

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 相同的主键值的小部件。

于 2012-10-25T20:00:25.523 回答