1

我在 Model First 方法中发现了关于在 .NET 4.0 下运行的 EF5 的以下问题:我有以下自动生成的实体:

Partial Public Class Notice

    Private _id As Integer
    Public Property id As Integer
        Get
            Return _id
        End Get
        Friend Set(ByVal value As Integer)
            _id = value
        End Set
    End Property
    Public Property order_id As Integer
    Public Property employee_id As Integer
    Public Property sysdate As Datetime
    Public Property content As String

    Public Overridable Property order As Order
    Public Overridable Property employee As Employee

End Class

通知实体与订单实体和员工实体通过1(订单,员工)对多(通知)的关系关联。

之后,Order 实体也与 Employee 实体有关联:many (Order) to 1 (Employee) 关系。

然后,我预计以下单元测试会失败,因为通知实体与员工实体违规的关系(我没有分配 notice1.employee 导航属性):

<TestMethod()>
    <ExpectedException(GetType(DbUpdateException))>
    Public Sub ShouldNotAllowSaveNoticeWithoutAssignedEmployee()

        Dim notice1 = CreateNewNotice() ' returned entity has not set any relation
        notice1.order= CreateNewOrderWithAllRequiredAndRelatedEntities()

        DbContext.noticeSet.Add(notice1)
        DbContext.SaveChanges()

    End Sub

但在结果测试中通过了。在数据库中,Notice->employee_id 值等于 Order->employee_id 值,这是意料之外的,因为这些外键可能指向不同的 Employee 对象。DbUpdateException我希望我必须自己设置 notice1.employee 导航属性,如果我忘记这样做,我想获得例外。

这种奇怪的 EF5 行为的原因是什么?

更新:

CreateNewNotice()CreateNewOrderWithAllRequiredAndRelatedEntities()实施:

 Protected Function CreateNewNotice(Optional ByVal suffix As Int32 = 1) As Notice

        Dim notice1 = New Notice() With {
            .sysdate = DateTime.Now,
            .content = Description & suffix ' Description is constant, for testing purposes
        }

        Return notice1 

    End Function

Protected Function CreateNewOrderWithAllRequiredAndRelatedEntities(Optional ByVal suffix As Int32 = 1) As Order

        Dim order1 = CreateNewOrder(suffix) ' returned entity has not set any relation
        order1.employee = CreateNewEmployee(suffix)
        order1.customer = CreateNewCustomerWithAllRequiredRelatedEntities(suffix)
        order1.seller = CreateNewSeller(suffix)

        For i As Integer = 1 To 3
            order1.notices.Add(CreateNewNotice(i)) ' created notices have not initialized relations
        Next

        Return order1

    End Function
4

1 回答 1

1

我已经认识到这个问题,并且看起来 EF5 的行为不同,具体取决于我们使用的是“外键关联”还是“独立关联”(有关 EF 中关系的一些信息:Relationships and Navigation Properties

假设我们使用 EF5 模型优先方法(使用 .NET 4.5)。让我们考虑自动生成的实体类,与主题问题中显示的示例相关:

Partial Public Class Employee
    Public Property id As Integer
    Public Property firstname As String
    Public Property lastname As String

    Public Overridable Property Notices As ICollection(Of Notice) = New HashSet(Of Notice)
    Public Overridable Property Orders As ICollection(Of Order) = New HashSet(Of Order)

End Class

Partial Public Class Order
    Public Property id As Integer
    Public Property Employee_id As Integer
    Public Property article As String

    Public Overridable Property Notices As ICollection(Of Notice) = New HashSet(Of Notice)
    Public Overridable Property Employee As Employee

End Class

Partial Public Class Notice
    Public Property id As Integer
    Public Property Employee_id As Integer
    Public Property Order_id As Integer
    Public Property sysdate As Date
    Public Property content As String

    Public Overridable Property Employee As Employee
    Public Overridable Property Order As Order

End Class

该实体类使用“外键关联”和“独立关联”。然后,我们有以下单元测试类:

<TestClass()>
Public Class UnitTests

    Protected DbContext As DbModelContext

    <TestInitialize()>
    Public Sub TestInitialize()

        Thread.CurrentThread.CurrentUICulture = New CultureInfo("en-us")
        DbContext = New DbModelContext()

    End Sub

    <TestCleanup()>
    Public Sub TestCleanup()

        DbContext.Dispose()

    End Sub

    <TestMethod()>
    Public Sub Test1()

        Dim notice1 = CreateNewNotice()
        notice1.Order = CreateNewOrderWithAllRequiredAndRelatedEntities()

        DbContext.NoticeSet.Add(notice1)
        DbContext.SaveChanges() ' no DbUpdateException exception

        Assert.AreEqual(notice1.Employee.id, notice1.Order.Employee.id)
        Assert.AreEqual(notice1.Employee.id, notice1.Order.Notices.First().Employee.id)

    End Sub

    <TestMethod()>
    Public Sub Test2()

        Dim notice1 = CreateNewNotice()
        notice1.Order = CreateNewOrderWithAllRequiredAndRelatedEntities()

        DbContext.NoticeSet.Add(notice1)

        Dim employee2 = CreateNewEmployee()

        DbContext.EmployeeSet.Add(employee2)

        DbContext.SaveChanges() 'DbUpdateException exception

    End Sub

    ''' <summary>
    ''' Create new Order object along with required associated objects,
    ''' however related Notice objects do not have assigned required associated objects
    ''' </summary>
    Protected Function CreateNewOrderWithAllRequiredAndRelatedEntities(Optional ByVal suffix As Int32 = 1) As Order

        Dim order1 = CreateNewOrder(suffix)
        order1.Employee = CreateNewEmployee(suffix)

        For i = suffix To suffix + 2

            order1.Notices.Add(CreateNewNotice(i))

        Next

        Return order1

    End Function

    ''' <summary>
    ''' Create new Order object without required associated objects
    ''' </summary>
    Protected Function CreateNewOrder(Optional ByVal suffix As Int32 = 1) As Order

        Dim order1 = New Order() With {
                .article = "article" & suffix
            }

        Return order1

    End Function

    ''' <summary>
    ''' Create new Employee object required without associated objects
    ''' </summary>
    Protected Function CreateNewEmployee(Optional ByVal suffix As Int32 = 1) As Employee

        Dim employee1 = New Employee() With {
                .firstname = "firstname" & suffix,
                .lastname = "lastname" & suffix
            }

        Return employee1

    End Function

    ''' <summary>
    ''' Create new Notice object without associated objects
    ''' </summary>
    Protected Function CreateNewNotice(Optional ByVal suffix As Int32 = 1) As Notice

        Dim notice1 = New Notice() With {
            .sysdate = DateTime.Now,
            .content = "Description" & suffix
        }

        Return notice1

    End Function

End Class

如果我们运行测试,Test1()通过,但Test2()异常失败:

System.Data.Entity.Infrastructure.DbUpdateException:无法确定“Model1.NoticeEmployee”关系的主体端。多个添加的实体可能具有相同的主键。---> System.Data.UpdateException:无法确定“Model1.NoticeEmployee”关系的主体端。多个添加的实体可能具有相同的主键。

结论:

里面Test1()只有一个Employee对象和一个Order对象DbContext。代码中未设置的关系 ( Notice.Employee, ) 由 EF 在语句Notice.Order内自动设置。DbContext.SaveChanges()里面Test2()有两个Employee对象DbContext,所以Notice.Employee没有自动赋值。

奇怪的是,如果我们从模型实体中删除外键属性,以便仅具有“独立关联”功能,则两个测试都会失败,并出现相同的System.Data.Entity.Infrastructure.DbUpdateException异常。

同样,如果我们从模型实体中删除导航属性,以便仅具有“外键关联”功能,则两个测试都会失败并出现相同的异常。

于 2013-04-03T19:44:07.020 回答