I have recognized this issue, and it looks like there is a different behavior of EF5 depending on whether we use 'foreign key associations' or 'independent associations' (some informations about relationships in EF: Relationships and Navigation Properties)
Lets assume that we use EF5 Model First Approach (with .NET 4.5). Lets take in consideration automatically generated entity classes, related to example showed up in topic's questions:
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
This entities classes use 'foreign key associations' and 'independent associations'. Then, we have the following unit tests 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
If we run tests, Test1()
passes, but Test2()
fails with exception:
System.Data.Entity.Infrastructure.DbUpdateException: Unable to determine the principal end of the 'Model1.NoticeEmployee' relationship. Multiple added entities may have the same primary key. ---> System.Data.UpdateException: Unable to determine the principal end of the 'Model1.NoticeEmployee' relationship. Multiple added entities may have the same primary key.
Conclusions:
Inside Test1()
there is only one Employee
object and one Order
object in the DbContext
. Relations which are not set in code (Notice.Employee
, Notice.Order
) are automatically set up by EF within DbContext.SaveChanges()
statement. Inside Test2()
there are two Employee
object in DbContext
, so Notice.Employee
has not automatically assigned value.
What is strange, if we remove foreign key properties from model entities, in order to have only 'independent associations' functionality, both tests fails with the same System.Data.Entity.Infrastructure.DbUpdateException exception.
Similarly, if we remove navigation properties from model entities, in order to have only 'foreign key associations' functionality, both tests fails with the same exception.