Search code examples
c#many-to-manyasp.net-core-webapicrudef-core-2.2

How to add/update many to many relations with one end of entity in EF core


We have visited quite a few links on EF Core many to many update, yet could not figure a concrete answer to our question and clear our understanding.

Scenario: We wish to add/update an entity and its related many to many relations in one go like (dbset.Add() or dbset.Update())

We were trying the following and could only add/update the parent entity and not the many-to-many relation list. Can you help us know where we are wrong? and what can be done?

Current Model Structure:


        public class Teacher
    {
        [DatabaseGenerated(DatabaseGeneratedOption.Identity), Required]
        public long Id { get; set; }
        public string Name { get; set; }
        public List<TeacherDuty> TeacherDuties { get; set; }
    }
    public class Duty
    {
        [DatabaseGenerated(DatabaseGeneratedOption.Identity), Required]
        public long Id { get; set; }
        public string Description { get; set; }
        public List<TeacherDuty> TeacherDuties { get; set; }
    }
    public class TeacherDuty
    {
        public long TeacherId { get; set; }
        public Teacher Teacher { get; set; }

        public long DutyId { get; set; }
        public Duty Duty { get; set; }
    }

And we are trying to add/update using following methods:

public async Task<Teacher> AddTeacher(Teacher pTeacher)
{
    try
    {
        return await _teacher.AddAsync(pTeacher);
    }
    catch (Exception ex) { throw ex; }
}

public async Task<Teacher> UpdateTeacher(Teacher pTeacher)
{
    try
    {
        return await _teacher.Update(pTeacher);
    }
    catch (Exception ex) { throw ex; }
}

Kindly point us to our misinterpretation of concept and solution if possible. Thanks.


Solution

  • I create a demo to add and edit a teacher.(_context is database context)

    Add a teacher:

    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> Create(Teacher teacher)
        {
            //get your desired dutyId with your own logic
            var SelectedDutyIds = new int[] { 1 };
            var teacherDuties = new List<TeacherDuty>();
    
            if (ModelState.IsValid)
            {
                _context.Add(teacher);
                await _context.SaveChangesAsync();
    
    
                foreach (var id in SelectedDutyIds)
                {
                    var item = new TeacherDuty()
                    {
                        TeacherId = teacher.Id,
                        DutyId = id,
                    };
                    teacherDuties.Add(item);
                }
                _context.AddRange(teacherDuties);
                await _context.SaveChangesAsync();
                return RedirectToAction(nameof(Index));
            }
            return View(teacher);
        }
    

    Edit the teacher: remove all the existing TeacherDuties of the teacher firstly and then add new ones.

     [HttpPost]
     [ValidateAntiForgeryToken]
     public async Task<IActionResult> Edit(long id, Teacher teacher)
        {
            if (id != teacher.Id)
            {
                return NotFound();
            }
    
            if (ModelState.IsValid)
            {
                try
                {
                   //your new dutyIds
                    var newSelectedDutyIds = new int[] { 3 };
                    var teacherDuties = new List<TeacherDuty>();
    
    
                    var tdList = await _context.TeacherDuties.Where(td => td.TeacherId == teacher.Id).ToListAsync() ;
                    _context.RemoveRange(tdList);
    
                    foreach (var newid in newSelectedDutyIds)
                    {
                        var item = new TeacherDuty()
                        {
                            TeacherId = teacher.Id,
                            DutyId = newid,
                        };
                        teacherDuties.Add(item);
                    }
                    _context.AddRange(teacherDuties);
                    _context.Update(teacher);
    
                    await _context.SaveChangesAsync();
                }
                catch (DbUpdateConcurrencyException)
                {
                    if (!TeacherExists(teacher.Id))
                    {
                        return NotFound();
                    }
                    else
                    {
                        throw;
                    }
                }
                return RedirectToAction(nameof(Index));
            }
            return View(teacher);
        }
    

    Refer to Entity framework core update many to many