Home > database >  Entity Framework: Why can I not empty a collection in this Many-to-Many relations?
Entity Framework: Why can I not empty a collection in this Many-to-Many relations?

Time:04-17

I have the Course entity that holds a collection of Students, and they are in a Many-to-Many relationship, as per tutorial enter image description here

Course class:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Library
{
    public class Course
    {
        public Course()
        {
            this.Students = new HashSet<Student>();
        }

        public string CourseId { get; set; }
        public string CourseName { get; set; }

        public string Department {get; set;}

        public virtual ICollection<Student> Students { get; set; }
    }
}

Student class

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;

namespace Library
{
    public class Student
    {
        public Student() 
        {
            this.Courses = new HashSet<Course>();
        }

        public string StudentId { get; set; }

        [Required]
        public string StudentName { get; set; }
        public int Age { get; set; }

        public virtual ICollection<Course> Courses { get; set; }
    }
}

My method (azure function)

[FunctionName("UpdateCourse")]
public async Task<IActionResult> UpdateCourse(
[HttpTrigger(AuthorizationLevel.Anonymous, "put", Route = "courses/{CourseId}")]
HttpRequest req,
ILogger log, string CourseId) {

    var course = await _context.Courses.FindAsync(CourseId);

// course.Students.Clear();

    foreach (var item in course.Students)
    {
        course.Students.Remove(item);
    }


    _context.Update(course);

    await _context.SaveChangesAsync();

    return new OkObjectResult(removeSelfReference(course));
}

EDIT 1:

Here are parts of my code-first migration files for the join table CourseStudent

20220415034607_M1.cs:

migrationBuilder.CreateTable(
    name: "CourseStudent",
    columns: table => new
    {
        CoursesCourseId = table.Column<string>(type: "nvarchar(450)", nullable: false),
        StudentsStudentId = table.Column<string>(type: "nvarchar(450)", nullable: false)
    },
    constraints: table =>
    {
        table.PrimaryKey("PK_CourseStudent", x => new { x.CoursesCourseId, x.StudentsStudentId });
        table.ForeignKey(
            name: "FK_CourseStudent_Courses_CoursesCourseId",
            column: x => x.CoursesCourseId,
            principalTable: "Courses",
            principalColumn: "CourseId",
            onDelete: ReferentialAction.Cascade);
        table.ForeignKey(
            name: "FK_CourseStudent_Students_StudentsStudentId",
            column: x => x.StudentsStudentId,
            principalTable: "Students",
            principalColumn: "StudentId",
            onDelete: ReferentialAction.Cascade);
    });

20220415034607_M1.Designer.cs:

modelBuilder.Entity("CourseStudent", b =>
    {
        b.HasOne("Library.Course", null)
            .WithMany()
            .HasForeignKey("CoursesCourseId")
            .OnDelete(DeleteBehavior.Cascade)
            .IsRequired();

        b.HasOne("Library.Student", null)
            .WithMany()
            .HasForeignKey("StudentsStudentId")
            .OnDelete(DeleteBehavior.Cascade)
            .IsRequired();
    });

CodePudding user response:

Did you verify that your course is lazy loading students? If lazy loading is disabled then course.Students will be empty. When modifying the collection of students you should be eager-loading it regardless:

var course = context.Courses
    .Include(x => x.Students)
    .Single(x => x.CourseId == courseId);

From there you should be able to use:

course.Students.Clear(); 

since Course.Students will be a proxy that EF recognizes and the Clear method will mark all entries for removal. With many-to-many relationships you may need to dis-associate the course from each of the students:

var course = context.Courses
    .Include(x => x.Students)
    .Single(x => x.CourseId == courseId);

foreach(var student in course.Students)
    student.Courses.Remove(course);

course.Students.Clear();
context.SaveChanges();

to be sure that the references are removed in the event that any of the students might be marked as modified.

Lastly, with tracked entities, just call SaveChanges()rather than calling Update. Update is for untracked/detached entities.

  • Related