A Brief Discussion on Synchronizing Entity Framework Navigation Properties and Foreign Keys
TLDR
- The prerequisite for Navigation Property synchronization is that the related Entity must be in a Tracked state.
- Any operation that triggers change tracking verification (such as
Add,Entry,SaveChanges, orFind) will automatically synchronize navigation properties and foreign key properties. - Whether navigation properties are synchronized does not affect database correctness after
SaveChangesis executed; EF Core automatically performs verification before saving. - Using
main.Subs.Remove(sub)only removes the association; to delete child table data, you must usecontext.Subs.Remove(sub). - If the foreign key allows
null, removing the association will set the foreign key tonullinstead of deleting the child record.
Entity Structure Definition
This test uses Microsoft.EntityFrameworkCore 8.
csharp
public partial class Main {
public long Id { get; set; }
public virtual ICollection<Sub> Subs { get; set; } = new List<Sub>();
}
public partial class Sub {
public long Id { get; set; }
public long MainId { get; set; }
public virtual Main Main { get; set; }
}Associating Child Tables Using Navigation Properties in the Main Table
The Impact of Tracking State on Synchronization
When you might encounter this issue: Before calling SaveChanges(), you need to confirm whether the navigation property has been automatically associated.
- Untracked: If neither
mainnorsubare added to tracking,sub.Mainisnull. - Only Main Table Tracked: When
mainis added to tracking,subwill be tracked synchronously, andsub.Mainwill be updated automatically. - Only Child Table Tracked: If only
subis tracked andmainis not,sub.Mainwill not be updated synchronously. - Tracked Before Setting: If you track
mainfirst and then executemain.Subs.Add(sub),sub.Mainwill benullbeforeSaveChanges(), but it will synchronize automatically after execution.
Associating Main Tables Using Navigation Properties in the Child Table
When you might encounter this issue: When creating an association by directly manipulating the child table's navigation property.
- Untracked: If you set
sub.Main = maindirectly and neither is tracked,main.Subsremains an empty collection. - Only Main Table Tracked: When
mainis added to tracking butsubis not,main.Subsremains an empty collection. - Only Child Table Tracked: When only
subis tracked, EF will automatically trackmainsynchronously; at this point,main.Subswill containsub.
Setting Associations Using Foreign Key Properties
When you might encounter this issue: When creating an association by directly modifying the MainId field.
- Only Child Table Tracked: If only
subis tracked andMainIdis set, the navigation property will not synchronize. - Both Tracked: When both are tracked, the navigation property will synchronize automatically.
- Modify Foreign Key After Tracking: If
MainIdis set after adding to tracking, the navigation property will not synchronize beforeSaveChanges(), but it will be updated after execution. - Retrieving Data with Find(): When using
Find()to retrieve trackedMaindata, the navigation property will synchronize automatically; if theMainretrieved is untracked, it will not synchronize.
Other Operations and Behavior Analysis
SaveChanges Failure and Entry Calls
- SaveChanges Failure: Even if
SaveChanges()fails due to database constraints (such as duplicate primary keys), the internal navigation properties of EF will still complete synchronization. - Entry Trigger: Executing
context.Entry(entity)will force a change tracking verification, thereby synchronizing the navigation properties of tracked Entities.
Notes on Deleting Data
- Deleting Child Table: You must use
context.Subs.Remove(sub)to remove the data from the database. - Removing Association: Using
main.Subs.Remove(sub)only removes the association.- For many-to-many associations, this operation removes the record from the join table.
- If the foreign key allows
null, this operation will setsub.MainIdtonullwithout deleting the child table data.
Change Log
- 2024-08-12 Initial document creation.
- 2026-05-29 Added link to the corresponding GitHub sample project.