In the previous chapter, you learned how the ChangeTracker
automatically changes the EntityState
of each entity in the connected scenario.
Here, you will learn about the behaviours of different methods on the root entity and child entities of the disconnected entity graph in Entity Framework Core.
Entity Framework Core provides the following different methods, which not only attach an entity to a context, but also change the EntityState
of each entity in a disconnected entity graph:
Let's see how the above methods change the EntityState
of each entity in an entity graph in Entity Framework Core 2.x.
The DbContext.Attach()
and DbSet.Attach()
methods attach the specified disconnected entity graph and start tracking it.
They return an instance of EntityEntry
, which is used to assign the appropriate EntityState
.
The following example demonstrates the behaviour of the DbContext.Attach()
method on the EntityState
of each entity in a graph.
public static void Main() { var stud = new Student() { //Root entity (empty key) Name = "Bill", Address = new StudentAddress() //Child entity (with key value) { StudentAddressId = 1, City = "Seattle", Country = "USA" }, StudentCourses = new List<StudentCourse>() { new StudentCourse(){ Course = new Course(){ CourseName = "Machine Language" } },//Child entity (empty key) new StudentCourse(){ Course = new Course(){ CourseId = 2 } } //Child entity (with key value) } }; var context = new SchoolContext(); context.Attach(stud).State = EntityState.Added; DisplayStates(context.ChangeTracker.Entries()); } private static void DisplayStates(IEnumerable<EntityEntry> entries) { foreach (var entry in entries) { Console.WriteLine($"Entity: {entry.Entity.GetType().Name}, State: {entry.State.ToString()} "); } }
In the above example, stud
is an instance of the Student
entity graph which includes references of StudentAddress
and StudentCourse
entities.
context.Attach(stud).State = EntityState.Added
attaches the stud
entity graph to a context and sets Added state to it.
The Attach()
method sets Added EntityState
to the root entity (in this case Student
) irrespective of whether it contains the Key value or not.
If a child entity contains the key value, then it will be marked as Unchanged, otherwise it will be marked as Added.
The output of the above example shows that the Student
entity has Added EntityState
, the child entities with non-empty key values have Unchanged EntityState
and the ones with empty key values have Added state.
The following table lists the behaviour of the Attach()
method when setting a different EntityState
to a disconnected entity graph.
Attach() | Root entity with Key value | Root Entity with Empty or CLR default value | Child Entity with Key value | Child Entity with empty or CLR default value |
---|---|---|---|---|
context.Attach(entityGraph).State = EntityState.Added | Added | Added | Unchanged | Added |
context.Attach(entityGraph).State = EntityState.Modified | Modified | Exception | Unchanged | Added |
context.Attach(entityGraph).State = EntityState.Deleted | Deleted | Exception | Unchanged | Added |
The DbContext.Entry()
method behaves differently in Entity Framework Core compared with the previous EF 6.x. Consider the following example:
var student = new Student() { //Root entity (empty key) Name = "Bill", Address = new StudentAddress() //Child entity (with key value) { StudentAddressId = 1, City = "Seattle", Country = "USA" }, StudentCourses = new List<StudentCourse>() { new StudentCourse(){ Course = new Course(){ CourseName="Machine Language" } },//Child entity (empty key) new StudentCourse(){ Course = new Course(){ CourseId=2 } } //Child entity (with key value) } }; var context = new SchoolContext(); context.Entry(student).State = EntityState.Modified; DisplayStates(context.ChangeTracker.Entries());
In the above example, context.Entry(student).State = EntityState.Modified
attaches an entity to a context and applies the specified EntityState
(in this case, Modified) to the root entity, irrespective of whether it contains a Key property value or not.
It ignores all the child entities in a graph and does not attach or set their EntityState
.
The following table lists different behaviours of the DbContext.Entry()
method.
Set EntityState using Entry() | Root entity with Key value | Root Entity with Empty or CLR default value | Child Entities with/out Key value |
---|---|---|---|
context.Entry(entityGraph).State = EntityState.Added | Added | Added | Ignored |
context.Entry(entityGraph).State = EntityState.Modified | Modified | Modified | Ignored |
context.Entry(entityGraph).State = EntityState.Deleted | Deleted | Deleted | Ignored |
The DbContext.Add
and DbSet.Add
methods attach an entity graph to a context and set Added EntityState
to a root and child entities, irrespective of whether they have key values or not.
var student = new Student() { //Root entity (with key value) StudentId = 1, Name = "Bill", Address = new StudentAddress() //Child entity (with key value) { StudentAddressId = 1, City = "Seattle", Country = "USA" }, StudentCourses = new List<StudentCourse>() { new StudentCourse(){ Course = new Course(){ CourseName="Machine Language" } },//Child entity (empty key) new StudentCourse(){ Course = new Course(){ CourseId=2 } } //Child entity (with key value) } }; var context = new SchoolContext(); context.Students.Add(student); DisplayStates(context.ChangeTracker.Entries());
The following table lists possible EntityState of each entity in a graph using the DbContext.Add
or DbSet.Add
methods.
Method | Root entity with/out Key value | Child Entities with/out Key value |
---|---|---|
DbContext.Add(entityGraph) or DbSet.Add(entityGraph) | Added | Added |
The DbContext.Update()
and DbSet.Update()
methods attach an entity graph to a context and set the EntityState
of each entity in a graph depending on whether it contains a key property value or not. Consider the following example.
var student = new Student() { //Root entity (with key value) StudentId = 1, Name = "Bill", Address = new StudentAddress() //Child entity (with key value) { StudentAddressId = 1, City = "Seattle", Country = "USA" }, StudentCourses = new List<StudentCourse>() { new StudentCourse(){ Course = new Course(){ CourseName="Machine Language" } },//Child entity (empty key) new StudentCourse(){ Course = new Course(){ CourseId=2 } } //Child entity (with key value) } }; var context = new SchoolContext(); context.Update(student); DisplayStates(context.ChangeTracker.Entries());
In the above example, the Update()
method applies the Modified state to the entities which contain non-empty key property values and the Added state to those which contain empty or default CLR key values, irrespective of whether they are a root entity or a child entity.
Update() | Root entity with Key value | Root Entity with Empty or CLR default value | Child Entities with Key value | Child Entities with Empty Key value |
---|---|---|---|---|
DbContext.Update(entityGraph) or DbSet.Update(entityGraph) | Modified | Added | Modified | Added |
The DbContext.Remove()
and DbSet.Remove()
methods set the Deleted EntityState
to the root entity.
var student = new Student() { //Root entity (with key value) StudentId = 1, Name = "Bill", Address = new StudentAddress() //Child entity (with key value) { StudentAddressId = 1, City = "Seattle", Country = "USA" }, StudentCourses = new List<StudentCourse>() { new StudentCourse(){ Course = new Course(){ CourseName="Machine Language" } },//Child entity (empty key) new StudentCourse(){ Course = new Course(){ CourseId=2 } } //Child entity (with key value) } }; var context = new SchoolContext(); context.Remove(student); DisplayStates(context.ChangeTracker.Entries());
The following table lists the behaviour of the Remove()
method on the EntityState
of each entity.
Remove() | Root entity with Key value | Root Entity with Empty or CLR default value | Child Entities with Key value | Child Entities with Empty Key value |
---|---|---|---|---|
DbContext.Remove(entityGraph) or DbSet.Remove(entityGraph) | Deleted | Exception | Unchanged | Added |
Thus, be careful while using the above methods in EF Core.
Learn about the ChangeTracker.TrackGraph()
method to deal with each entity in an entity graph in the next chapter.