协会奇怪的行为

时间:2013-03-29 14:41:41

标签: vb.net entity-framework entity-framework-5 model-associations ef-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实体相关联。

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

然后,我希望以下单元测试失败,因为与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

但结果测试通过了。在数据库中,注意 - &gt; employee_id值等于Order-&gt; employee_id值,这是不期望的,因为这些外键可能指向不同的Employee对象。我本以为我必须自己设置notice1.employee导航属性,如果我忘记了,我想获得DbUpdateException异常。

这种奇怪的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

1 个答案:

答案 0 :(得分:1)

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

让我们假设我们使用EF5 Model First Approach(使用.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”关系的主要结尾。多个添加的实体可以具有相同的主键。 ---&GT; System.Data.UpdateException:无法确定“Model1.NoticeEmployee”关系的主要结尾。多个添加的实体可能具有相同的主键。

结论:

Test1()内,Employee中只有一个Order个对象和一个DbContext个对象。未在代码中设置的关系(Notice.EmployeeNotice.Order)由EF在DbContext.SaveChanges()语句中自动设置。在Test2()内,Employee中有两个DbContext对象,因此Notice.Employee未自动分配值。

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

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