.NET Core下EF5多对多关联添加学生到课程时原有成员丢失问题
问题根因
- 多次重复提交更改冲突:
AddStudent方法内部每次执行完关联操作就单独调用SaveChangesAsync提交,同时控制器末尾又调用了一次全局的_context.SaveChanges()。如果控制器开头查询的Course实例未加载最新的StudentsList数据,最终的全局提交会用旧数据覆盖之前已经提交的关联关系。 - 重复查询导致跟踪实例不一致:如果仓储的查询方法使用了
AsNoTracking,或者首次查询Course时没有Include关联的StudentsList,会导致内存中存在多个不同的Course实例,EF 提交更改时会以最后跟踪到的实例状态为准。 - 学生存在性判断逻辑不可靠:使用
Contains做引用类型比较,只要两个学生实例不是同一个跟踪对象,哪怕ID相同也会判定为不存在,判断逻辑失效。
修复方案
1. 调整提交逻辑,统一提交所有更改
删除 AddStudent 方法内部的 SaveChangesAsync 调用,所有实体修改完成后统一在控制器末尾提交,避免多次提交冲突。
2. 优化存在性判断逻辑
用学生ID比较替代引用比较,保证判断准确性。
3. 可选优化:减少重复查询
如果上下文是同一请求内共享的作用域实例,无需在 AddStudent 中重复查询 Course,直接复用控制器中已查询到的实例即可,降低数据库开销。
修改后的代码示例
仓储层 AddStudent 方法
public async Task<bool> AddStudent(long id, string studentId) { Course course = await _context.Courses.Include(c => c.StudentsList).FirstOrDefaultAsync(c => c.CourseId == id); Student student = await _context.Students.FirstOrDefaultAsync(s => s.StudentId == studentId); if (course == null || student == null) return false; // 改用ID判断是否已存在 if (!course.StudentsList.Any(s => s.StudentId == studentId)) { course.StudentsList.Add(student); } return true; }
控制器调整
[HttpPatch("{id}")] public async Task<IActionResult> RegisterStudentToCourse(long id, [FromBody] List<string> studentsIds) { // 查询Course时必须Include StudentsList,保证拿到最新的关联数据 Course course = await _courseRepo.GetCourse(id); if (course == null || !studentsIds.Any()) return BadRequest(); IEnumerable<Lesson> list = await _lessonRepo.GetLessons(id); List<Lesson> lessonsList = list.ToList(); foreach (string studentId in studentsIds) { Student student = _studetRepo.GetStudent(studentId); if (student == null) return BadRequest(); // 改用ID判断是否已存在 if (course.StudentsList.Any(s => s.StudentId == studentId)) continue; await _courseRepo.AddStudent(id, studentId); foreach (Lesson l in lessonsList) { Attendance attendance = await _attendanceRepo.CreateAttandance(studentId); await _lessonRepo.AddAttendance(l.LessonId, attendance); } } // 所有修改完成后统一提交一次即可 await _context.SaveChangesAsync(); return Ok(); }
可选:模型属性初始化(避免空引用异常)
public class Course { public long CourseId { get; set; } public string CourseName { get; set; } public IEnumerable<Lesson> LessonsList { get; set; } public DateTime CourseStartDate { get; set; } public DateTime CourseEndDate { get; set; } // 初始化关联集合,避免空引用 public List<Student> StudentsList { get; set; } = new List<Student>(); } public class Student { [Key] public string StudentId { get; set; } public List<Course> Courses { get; set; } = new List<Course>(); }
内容的提问来源于stack exchange,提问作者Ziv har-lev
相关产品推荐
相关产品推荐

