【问题标题】:update data in the junction table更新联结表中的数据
【发布时间】:2020-07-02 05:18:16
【问题描述】:

ASP.Net Core 与 MySQL

Student 表和 Course 表通过联结表 StudentCourse 连接:

Student      StudentCourse       Course
=========    ============        =======
PK | ID      FK | StudentID       PK | ID
   | Name    FK | CourseID           | Name

学生更改了他的课程,因此经理需要更改连接表 StudentCourse 中的 CourseID。

但由于以下原因,我无法直接访问联结表:

public class AppDbContext
{
    public DbSet<CourseModel> CourseModels { get; set; }
    public DbSet<StudentModel> StudentModels { get; set; }

    protected override void OnModelCreating(ModelBuilder builder)
    {
        base.OnModelCreating(builder);

        builder.Entity<StudentCourse>()
            .HasKey(t => new { t.StudentId, t.CourseId });

        builder.Entity<StudentCourse>()
            .HasOne(sc => sc.Student)
            .WithMany(s => s.StudentCourse)
            .HasForeignKey(sc => sc.StudentId);

        builder.Entity<StudentCourse>()
            .HasOne(sc => sc.Course)
            .WithMany(s => s.StudentCourse)
            .HasForeignKey(sc => sc.CourseId);
    }
}

所以我不能直接执行这个SQL命令:

UPDATE StudentCourse SET CourseId = '9A414E30-DA37-48ED-105E-08D8129A967A' WHERE StudentId = 'A1F38C12-AE65-464C-C489-08D814F4CDDC'

我想我必须使用 Inner Join。我的逻辑是:

    [HttpGet]
    public IActionResult ChangeCourse(Guid id)
    {
        IQueryable<CourseModel> courses = courseModel.GetCourses();
        ViewBag.Courses = new SelectList(courses, "Id", "Name");

        var course = courseModel.GetCourseById(id);

        return View(course);
    }

    [HttpPost]
    public IActionResult ChangeCourse(CourseModel newCourse)
    {
        // I know student's id, so I can get his old course id
        Guid oldCourseId = (from ... in ...).SingleOrDefault();
        // Then just change and save
        oldCourseId = newCourse.Id;
        context.SaveChanges();

        return RedirectToAction();
    }

我有获取旧课程 ID 的 SQL 命令:

SELECT CourseId FROM StudentModels L
INNER JOIN StudentCourse R
ON L.Id = R.StudentId
WHERE L.Id = 'A1F38C12-AE65-464C-C489-08D814F4CDDC'

所以问题是:

  1. 我的假设是否正确?
  2. 如何在 LINQ 中编写这个 SQL 命令?

我试过了,但得到了 ID 列表:

from student in context.StudentModels join cours in context.CourseModels on student.Id equals studentId select cours.Id

我还不擅长 LINQ。请帮忙。

【问题讨论】:

  • 一名学生更改了他的课程,因此经理需要更改联结表 StudentCourse 中的 CourseID。 考虑另一种解决方案 - 在StudentCourse 中添加一列,定义当前记录是实际的或过时的。例如,这可以是 boolean (tinyint) Validity 列,其值为 1 或 0 表示该行是实际的或过时的。然后只需使用此附加列的条件。并且不需要更新,学生选择新课程时必须添加新行,学生退出课程时必须将行标记为无效。
  • 感谢您的回答。希望我明白你的意思。因为我们学校有很多学生,我担心这种情况下桌子会很大。例如,学生 Kate 正在访问课程 A 和课程 B。所以我们有两行。然后她完成了课程A并进入课程C。我认为最好只修改联结表中的数据:将A更改为C。否则课程A仍然会在表中。我们不会使用这些信息(以前的课程)。
  • 因为我们学校有很多学生,我担心在这种情况下表格会很大。 MySQL 中的“巨大”一词大约从 10kk 行开始。我怀疑你会达到它。存储完整历史的好处看起来更有帮助。 我们不会使用这些信息(以前的课程)。 这是当前的情况。您尚未考虑任何分析 - 但 99% 的任务将在未来出现。

标签: c# mysql .net linq asp.net-core


【解决方案1】:

学生和课程之间似乎存在多对多的关系。每个学生都遵循零个或多个课程,每个课程都跟随零个或多个学生。这就是为什么你需要一个联结表

你写道:

一个学生改变了他的课程

嗯,由于学生可以学习几门课程,因此您不能说学生更改了他的课程。您可以说学生学习了一门新课程,和/或停止学习另一门课程。

如果您遵循实体框架约定,您将拥有类似于以下的类:

class Student
{
    public int Id {get; set;}
    ...

    // Every Student follows zero or more Courses (many-to-many)
    public virtual ICollection<Course> Courses {get; set;}
}

class Course
{
    public int Id {get; set;}
    ...

    // Every Course is followed by zero or more Courses (many-to-many)
    public virtual ICollection<Student> Students {get; set;}
}

这是检测多对多关系所需的所有实体框架。它会自动为您创建一个联结表,并在需要时使用它。

如果您想更改实体框架中的任何项目,您必须获取完整的项目,并使用Include 获取相关集合

停止学习课程并开始学习新课程:

int studentId = ...
int courseIdToStop = ...
int courseIdToAttend = ...

// get the student to change; include his courses
Student studentToChange = dbContext.Students
    .Include(student => student.Courses)
    .Where(student => student.Id == studentId)
    .FirstOrDefault();


// Stop the course
Course courseToStop = studentToChange.Courses
    .Where(course => course.Id == courseIdToStop)
    .FirstOrDefault();

if (courseToStop != null)
{
    // This Student follows this cours; stop it:
    studentToChange.Courses.Remove(courseToStop);
}

Course courseToStart = dbContext.Courses
    .Where(course => course.Id == courseIdToStart)
    .FirstOrDefault();

// only start the course if course exists and not already following
if (courseToStart != null && !studentToChange.Courses.Contains(courseToStart))
{
    studentToChange.Courses.Add(courseToStart);
}

dbContext.SaveChanges();

【讨论】:

  • 感谢您的回答。 为什么需要联结表 这是实体框架检测多对多关系所需的全部知识。它会自动为您创建一个联结表,并在需要时使用它。 据我所知,EF Core 无法自动创建联结表。我说的对吗?
  • 我不熟悉核心版本的限制。我只使用完整的实体框架。为什么不在学校、教师和学生的小项目中进行测试。给他们一个地址,大约半小时后,你就测试了所有的关系:一对多、多对多、一对一。这也是一个测试未来想法或解释如何与同事建立关系的巧妙项目
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-01-12
  • 1970-01-01
  • 2012-03-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多