Update One-to-Many Entity using DBContext

We will see how to update existing Standard and Teacher entities in disconnected scenario which has One-to-Many relationship.

Entity Framework Tutorial
[Standard and Teacher has One-to-Many relationship]

The same way as we did it in the case of One-to-One entity relationship, here also we have to find which teacher entities are added, modified or removed from the collection property of Standard entity in disconnected scenario.

Consider following scenario in disconnected mode for One-to-Many entity relationship:

So you have to find entity state of each entities in the collection.

Below code snippet shows how you can handle above scenario and update One-to-Many entities:

using (var dbCtx = new SchoolDBEntities())
{
    //1- Get fresh data from database
    var existingStudent = dbCtx.Students.AsNoTracking().Include(s => s.Standard).Include(s => s.Standard.Teachers).Where(s => s.StudentName == "updated student").FirstOrDefault<Student>();

    var existingTeachers = existingStudent.Standard.Teachers.ToList<Teacher>();

    var updatedTeachers = teachers.ToList<Teacher>();
            
    //2- Find newly added teachers by updatedTeachers (teacher came from client sided) - existingTeacher = newly added teacher
    var addedTeachers = updatedTeachers.Except(existingTeachers, tchr => tchr.TeacherId);

    //3- Find deleted teachers by existing teachers - updatedTeachers = deleted teachers
    var deletedTeachers = existingTeachers.Except(updatedTeachers, tchr => tchr.TeacherId);

    //4- Find modified teachers by updatedTeachers - addedTeachers = modified teachers
    var modifiedTeacher = updatedTeachers.Except(addedTeachers, tchr => tchr.TeacherId);

    //5- Mark all added teachers entity state to Added
    addedTeachers.ToList<Teacher>().ForEach(tchr => dbCtx.Entry(tchr).State = System.Data.EntityState.Added);

    //6- Mark all deleted teacher entity state to Deleted
    deletedTeachers.ToList<Teacher>().ForEach(tchr => dbCtx.Entry(tchr).State = System.Data.EntityState.Deleted);


    //7- Apply modified teachers current property values to existing property values
    foreach(Teacher teacher in modifiedTeacher)
    {
        //8- Find existing teacher by id from fresh database teachers
        var existingTeacher = dbCtx.Teachers.Find(teacher.TeacherId);
                
        if (existingTeacher != null)
        {
            //9- Get DBEntityEntry object for each existing teacher entity
            var teacherEntry = dbCtx.Entry(existingTeacher);
            //10- overwrite all property current values from modified teachers' entity values, 
            //so that it will have all modified values and mark entity as modified
            teacherEntry.CurrentValues.SetValues(teacher);
        }

    }
    //11- Save all above changed entities to the database
    dbCtx.SaveChanges();
}
        

We did following steps in above code to handle all scenarios:

  1. Get fresh existing data from database
  2. Find newly added teachers by updatedTeachers (teacher came from client sided) - existingTeacher = newly added teacher
  3. Find deleted teachers by existing teachers - updatedTeachers = deleted teachers
  4. Find modified teachers by updatedTeachers - addedTeachers = modified teachers
  5. Mark all added teachers entity state to Added
  6. Mark all deleted teacher entity state to Deleted
  7. Apply modified teachers current property values to existing property values
  8. Find existing teacher by id from fresh database teachers
  9. Get DBEntityEntry object for each existing teacher entity
  10. Overwrite all property current values from modified teachers' entity values, so that it will have all modified values and mark entity as modified
  11. Save all above changed entities to the database

Note: we have used following extension method to compare values from two lists and return entities from one list which is not present into second list. Comparison happening based on passed function:

public static IEnumerable<T> Except<T, TKey>(this IEnumerable<T> items, IEnumerable<T> other, 
                                                                            Func<T, TKey> getKey)
{
    return from item in items
            join otherItem in other on getKey(item)
            equals getKey(otherItem) into tempItems
            from temp in tempItems.DefaultIfEmpty()
            where ReferenceEquals(null, temp) || temp.Equals(default(T))
            select item;

}
        

So this way you can update One-to-Many relationship entities.

In the next chapter we will learn how to update many-to-many relationship entities.