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