为何EF迁移时自动生成EmployeesProjects表?已自定义关联表
问题:EF Core迁移自动生成多余的多对多关联表
我有Employees和Projects两个实体,二者为多对多关系。我已自定义ProjectEmployee作为关联表,但执行Entity Framework迁移时,系统仍自动生成EmployeesProjects表。手动删除迁移代码中的该表创建逻辑后,再次执行迁移仍会生成它,排查所有配置文件未发现问题,请问原因及解决办法?
实体代码
Employees.cs
using Day1.Model; using System.ComponentModel.DataAnnotations; public class Employees { [Required] public int Id { get; set; } [Required] [MaxLength(50)] public string Name { get; set; } public int DepartmentId { get; set; } public DateTime JoinedDate { get; set; } public Salaries Salary { get; set; } public IList<Projects> Projects { get; set; } public Departments Department { get; set; } public IList<ProjectEmployee> ProjectEmployees { get; set; } }
Projects.cs
using Day1.Model; using System.ComponentModel.DataAnnotations; public class Projects { [Required] public int Id { get; set; } [Required] [MaxLength(50)] public string Name { get; set; } public IList<Employees> Employees { get; set; } public IList<ProjectEmployee> ProjectEmployees { get; set; } }
ProjectEmployee.cs
namespace Day1.Model { public class ProjectEmployee { public int ProjectId { get; set; } public Projects Project { get; set; } public int EmployeeId { get; set; } public Employees Employee { get; set; } public bool Enable { get; set; } } }
生成的迁移代码
using System; using Microsoft.EntityFrameworkCore.Migrations; #nullable disable #pragma warning disable CA1814 // Prefer jagged arrays over multidimensional namespace Day1.Migrations { /// <inheritdoc /> public partial class InitialCreate : Migration { /// <inheritdoc /> protected override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.CreateTable( name: "Departments", columns: table => new { Id = table.Column<int>(type: "int", nullable: false) .Annotation("SqlServer:Identity", "1, 1"), Name = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: false) }, constraints: table => { table.PrimaryKey("PK_Departments", x => x.Id); }); migrationBuilder.CreateTable( name: "Projects", columns: table => new { Id = table.Column<int>(type: "int", nullable: false) .Annotation("SqlServer:Identity", "1, 1"), Name = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: false) }, constraints: table => { table.PrimaryKey("PK_Projects", x => x.Id); }); migrationBuilder.CreateTable( name: "Employees", columns: table => new { Id = table.Column<int>(type: "int", nullable: false) .Annotation("SqlServer:Identity", "1, 1"), Name = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: false), DepartmentId = table.Column<int>(type: "int", nullable: false), JoinedDate = table.Column<DateTime>(type: "datetime2", nullable: false) }, constraints: table => { table.PrimaryKey("PK_Employees", x => x.Id); table.ForeignKey( name: "FK_Employees_Departments_DepartmentId", column: x => x.DepartmentId, principalTable: "Departments", principalColumn: "Id", onDelete: ReferentialAction.Cascade); }); migrationBuilder.CreateTable( name: "EmployeesProjects", columns: table => new { EmployeesId = table.Column<int>(type: "int", nullable: false), ProjectsId = table.Column<int>(type: "int", nullable: false) }, constraints: table => { table.PrimaryKey("PK_EmployeesProjects", x => new { x.EmployeesId, x.ProjectsId }); table.ForeignKey( name: "FK_EmployeesProjects_Employees_EmployeesId", column: x => x.EmployeesId, principalTable: "Employees", principalColumn: "Id", onDelete: ReferentialAction.Cascade); table.ForeignKey( name: "FK_EmployeesProjects_Projects_ProjectsId", column: x => x.ProjectsId, principalTable: "Projects", principalColumn: "Id", onDelete: ReferentialAction.Cascade); }); migrationBuilder.CreateTable( name: "ProjectEmployees", columns: table => new { ProjectId = table.Column<int>(type: "int", nullable: false), EmployeeId = table.Column<int>(type: "int", nullable: false), Enable = table.Column<bool>(type: "bit", nullable: false) }, constraints: table => { table.PrimaryKey("PK_ProjectEmployees", x => new { x.ProjectId, x.EmployeeId }); table.ForeignKey( name: "FK_ProjectEmployees_Employees_EmployeeId", column: x => x.EmployeeId, principalTable: "Employees", principalColumn: "Id", onDelete: ReferentialAction.Cascade); table.ForeignKey( name: "FK_ProjectEmployees_Projects_ProjectId", column: x => x.ProjectId, principalTable: "Projects", principalColumn: "Id", onDelete: ReferentialAction.Cascade); }); migrationBuilder.CreateTable( name: "Salaries", columns: table => new { Id = table.Column<int>(type: "int", nullable: false) .Annotation("SqlServer:Identity", "1, 1"), EmployeeId = table.Column<int>(type: "int", nullable: false), Salary = table.Column<int>(type: "int", nullable: false) }, constraints: table => { table.PrimaryKey("PK_Salaries", x => x.Id); table.ForeignKey( name: "FK_Salaries_Employees_EmployeeId", column: x => x.EmployeeId, principalTable: "Employees", principalColumn: "Id", onDelete: ReferentialAction.Cascade); }); migrationBuilder.InsertData( table: "Departments", columns: new[] { "Id", "Name" }, values: new object[,] { { 1, "Software Development" }, { 2, "Finance" }, { 3, "Accountant" }, { 4, "HR" } }); migrationBuilder.CreateIndex( name: "IX_Employees_DepartmentId", table: "Employees", column: "DepartmentId"); migrationBuilder.CreateIndex( name: "IX_EmployeesProjects_ProjectsId", table: "EmployeesProjects", column: "ProjectsId"); migrationBuilder.CreateIndex( name: "IX_ProjectEmployees_EmployeeId", table: "ProjectEmployees", column: "EmployeeId"); migrationBuilder.CreateIndex( name: "IX_Salaries_EmployeeId", table: "Salaries", column: "EmployeeId", unique: true); } /// <inheritdoc /> protected override void Down(MigrationBuilder migrationBuilder) { migrationBuilder.DropTable( name: "EmployeesProjects"); migrationBuilder.DropTable( name: "ProjectEmployees"); migrationBuilder.DropTable( name: "Salaries"); migrationBuilder.DropTable( name: "Projects"); migrationBuilder.DropTable( name: "Employees"); migrationBuilder.DropTable( name: "Departments"); } } }
原因分析
你同时在Employees和Projects实体中定义了两种关联逻辑:
- 直接的多对多导航属性:
Employees.Projects和Projects.Employees - 基于自定义关联表的导航属性:
Employees.ProjectEmployees和Projects.ProjectEmployees
EF Core会将第一种直接的多对多关系识别为需要自动生成默认关联表,第二种则对应你自定义的ProjectEmployee表,因此会同时生成两个关联表。
解决办法
方法一:移除直接的多对多导航属性
删除Employees中的public IList<Projects> Projects { get; set; }和Projects中的public IList<Employees> Employees { get; set; },只保留自定义关联表的导航属性。
修改后的Employees.cs:
using Day1.Model; using System.ComponentModel.DataAnnotations; public class Employees { [Required] public int Id { get; set; } [Required] [MaxLength(50)] public string Name { get; set; } public int DepartmentId { get; set; } public DateTime JoinedDate { get; set; } public Salaries Salary { get; set; } public Departments Department { get; set; } public IList<ProjectEmployee> ProjectEmployees { get; set; } }
修改后的Projects.cs:
using Day1.Model; using System.ComponentModel.DataAnnotations; public class Projects { [Required] public int Id { get; set; } [Required] [MaxLength(50)] public string Name { get; set; } public IList<ProjectEmployee> ProjectEmployees { get; set; } }
方法二:通过Fluent API显式配置多对多关系
如果需要保留直接的多对多导航属性,可以在DbContext的OnModelCreating方法中,显式指定使用自定义的ProjectEmployee表作为关联表,覆盖EF Core的默认行为:
protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Employees>() .HasMany(e => e.Projects) .WithMany(p => p.Employees) .UsingEntity<ProjectEmployee>( j => j.HasOne(pe => pe.Project).WithMany(p => p.ProjectEmployees), j => j.HasOne(pe => pe.Employee).WithMany(e => e.ProjectEmployees)); }
后续操作
修改完成后,需要删除之前的迁移文件并重新生成:
- 删除
Migrations文件夹下的所有迁移文件(若已执行过迁移,需同时删除数据库中__EFMigrationsHistory表的对应记录) - 执行命令生成新迁移:
Add-Migration InitialCreate(Package Manager Console)或dotnet ef migrations add InitialCreate(CLI) - 执行迁移更新数据库:
Update-Database(Package Manager Console)或dotnet ef database update(CLI)
内容的提问来源于stack exchange,提问作者Phương Nguyễn
相关产品推荐
相关产品推荐

