【问题标题】:EF 6 and many to many relationship [duplicate]EF 6和多对多关系[重复]
【发布时间】:2021-03-03 02:58:27
【问题描述】:

我正在研究 EF 6(映射多对多关系),请参阅

https://www.codeproject.com/Articles/234606/Creating-a-Many-To-Many-Mapping-Using-Code-First

它在哪里创建“PersonCourses”作为中间表,现在我有两个问题在多对多关系中,EF 在内部管理连接表并隐藏。这是一个在您的模型中没有实体类的表。

如果我需要在我的代码(项目)中访问“PersonCourses”,如果我需要添加某些列怎么办??

【问题讨论】:

    标签: sql-server entity-framework key many-to-many


    【解决方案1】:

    EF 可以自动管理连接表,只要它只包含两个 FK 作为复合 PK。如果要添加,则需要将连接表声明为每侧一对多的实体。

    所以而不是:

    [Table("Persons")]
    public class Person
    {
        // ...
        public virtual ICollection<Course> Courses { get; set; } = new List<Course>();
    }
    
    [Table("Courses")]
    public class Course
    {
        // ...
        public virtual ICollection<Person> People { get; set; } = new List<Person>();
    }
    
    modelBuilder.Entity<Person>()
        .HasMany(x => x.Courses)
        .WithMany(x => x.People);
    

    你会:

    [Table("Persons")]
    public class Person
    {
        // ...
        public virtual ICollection<PersonCourse> PersonCourses { get; set; } = new List<PersonCourse>();
    }
    
    [Table("Courses")]
    public class Course
    {
        // ...
        public virtual ICollection<PersonCourse> PersonCourses { get; set; } = new List<PersonCourse>();
    }
    
    [Table("PersonCourses")]
    public class PersonCourse
    {   
        [Key, Column(Order=0), ForeignKey("Person")]
        public int PersonId { get; set; }
        [Key, Column(Order=1), ForeignKey("Course")]
        public int CourseId { get; set; }
    
        // ... any additional properties for the entity.
    
        public virtual Person Person { get; set; }
        public virtual Course Course { get; set; }
    }
    
    
    modelBuilder.Entity<Person>()
        .HasMany(x => x.PersonCourses)
        .WithRequired(x => x.Person);
    modelBuilder.Entity<Course>()
        .HasMany(x => x.PersonCourses)
        .WithRequired(x => x.Course);
    

    这样做的主要缺点是 Person 不再有 Courses 的集合,而是 PersonCourses 的集合,因此每次想要获取有关课程名称的详细信息时,您都必须在预测中深入研究。将 PersonCourses 集合留在名为“Courses”的 Person 上可能很诱人,但我发现这可能会产生误导,因为您最终可能会在某些对象上收集称为 Persons 是 Person vs. PersonCourse 或 Courses 是 Course vs. PersonCourse。当集合名称反映类型时,通常不会令人困惑。

    所以而不是:

    var courses = context.Persons
        .Where(x => x.PersonId == personId)
        .SelectMany(x => x.Courses)
        .ToList();
    

    您需要将其更改为:

    var courses = context.Persons
        .Where(x => x.PersonId == personId)
        .SelectMany(x => x.PersonCourses.Select(pc => pc.Course))
        .ToList();
    

    更新:在 PersonCourse 上有一个 Id 列:

    [Table("PersonCourses")]
    public class PersonCourse
    {   
        [Key]
        public int Id { get; set; }
        [ForeignKey("Person")]
        public int PersonId { get; set; }
        [ForeignKey("Course")]
        public int CourseId { get; set; }
    
        // ... any additional properties for the entity.
    
        public virtual Person Person { get; set; }
        public virtual Course Course { get; set; }
    }
    

    ...或者更好的是,取消实体中的 FK 字段并通过配置映射它们:

    [Table("PersonCourses")]
    public class PersonCourse
    {   
        [Key]
        public int Id { get; set; }
    
        // ... any additional properties for the entity.
    
        public virtual Person Person { get; set; }
        public virtual Course Course { get; set; }
    }
    

    EF6 可能会按照约定自动映射这些,但您可以明确地使用:

    modelBuilder.Entity<Person>()
        .HasMany(x => x.PersonCourses)
        .WithRequired(x => x.Person)
        .Map(x => x.Mpakey("PersonId");
    modelBuilder.Entity<Course>()
        .HasMany(x => x.PersonCourses)
        .WithRequired(x => x.Course)
        .Map(x => x.Mpakey("CourseId");
    

    我推荐这种方法,例如带有 EF Core 的 Shadow Properties,以避免 Person ID 或 Course ID 有 2 个真实来源。

    PersonCourse.PersonIdPersonCourse.Person.Id

    当使用导航属性更新实体时,您应该通过导航属性(personCourse.Course = newCourse)而不是通过 FK 属性更新引用。 (personCourse.CourseId = newCourseId) 这样做或混合 FK 的事实来源可能会导致奇怪的结果,具体取决于 DbContext 当时跟踪的内容。

    【讨论】:

    • 如果我需要在 PersonCourses 表中添加 ID 等列
    • 没关系,将 ID 设置为 [Key],然后将 PersonId 和 CourseId 作为 FK 留给各自的导航属性。
    • 我已经扩展了答案以概述该更改并建议避免完全声明 FK 属性。
    猜你喜欢
    • 1970-01-01
    • 2014-05-19
    • 2021-10-26
    • 1970-01-01
    • 1970-01-01
    • 2022-10-17
    • 2021-10-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多