我在模型第一种方法中发现了关于在.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
答案 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.Employee
,Notice.Order
)由EF在DbContext.SaveChanges()
语句中自动设置。在Test2()
内,Employee
中有两个DbContext
对象,因此Notice.Employee
未自动分配值。
奇怪的是,如果我们从模型实体中删除外键属性,为了只具有“独立关联”功能,两个测试都会失败并使用相同的 System.Data.Entity.Infrastructure.DbUpdateException 异常。
同样,如果我们从模型实体中删除导航属性,为了只具有“外键关联”功能,两个测试都会失败并出现相同的异常。