EF Core多外键级联删除报错,Blazor示例却正常,求解析
我看到很多帖子提到,搭配SQL Server的EF Core会阻止带有OnDelete Cascade的多个外键,原因是存在循环或多重删除路径。但在VS2022的Blazor示例Web应用默认用户管理数据库中,这种配置却能正常运行且无报错。
我的测试实体类(简化版)
namespace BlazorSampleEFCore.EFCoreTrial { public class Company { [Key] public int DbKey { get; set; } public string Name { get; set; } public string Description { get; set; } public List<Employee> Employees { get; set; } public List<Group> Groups { get; set; } } public class Employee { [Key] public int DbKey { get; set; } public DateTime dob { get; set; } public string email { get; set; } [ForeignKey("CompanyId")] public int CompanyId { get; set; } public Company Company { get; set; } [ForeignKey("GroupId")] public int GroupID { get; set; } public Group Group { get; set; } } public class Group { [Key] public int DbKey { get; set; } public string Name { get; set; } public string Description { get; set; } public List<Employee> Employees { get; set;} public int CompanyId { get; set; } } }
示例数据库中AspNetUserRoles的迁移代码
该表有两个非空外键指向不同主表,且均设置OnDelete cascade:
migrationBuilder.CreateTable( name: "AspNetUserRoles", columns: table => new { UserId = table.Column<string>(type: "nvarchar(450)", nullable: false), RoleId = table.Column<string>(type: "nvarchar(450)", nullable: false) }, constraints: table => { table.PrimaryKey("PK_AspNetUserRoles", x => new { x.UserId, x.RoleId }); table.ForeignKey( name: "FK_AspNetUserRoles_AspNetRoles_RoleId", column: x => x.RoleId, principalTable: "AspNetRoles", principalColumn: "Id", onDelete: ReferentialAction.Cascade); table.ForeignKey( name: "FK_AspNetUserRoles_AspNetUsers_UserId", column: x => x.UserId, principalTable: "AspNetUsers", principalColumn: "Id", onDelete: ReferentialAction.Cascade); });
我的Employee表迁移代码
migrationBuilder.CreateTable( name: "Employee", columns: table => new { DbKey = table.Column<int>(type: "int", nullable: false) .Annotation("SqlServer:Identity", "1, 1"), dob = table.Column<DateTime>(type: "datetime2", nullable: false), email = table.Column<string>(type: "nvarchar(max)", nullable: false), CompanyId = table.Column<int>(type: "int", nullable: false), GroupID = table.Column<int>(type: "int", nullable: false) }, constraints: table => { table.PrimaryKey("PK_Employee", x => x.DbKey); table.ForeignKey( name: "FK_Employee_Companies_CompanyId", column: x => x.CompanyId, principalTable: "Companies", principalColumn: "DbKey", onDelete: ReferentialAction.Cascade); table.ForeignKey( name: "FK_Employee_Group_GroupID", column: x => x.GroupID, principalTable: "Group", principalColumn: "DbKey", onDelete: ReferentialAction.Cascade); });
执行Update-Database时的错误信息
fail: Microsoft.EntityFrameworkCore.Database.Command[20102]
Failed executing DbCommand (4ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
CREATE TABLE [Employee] (
[DbKey] int NOT NULL IDENTITY,
[NameDbKey] int NOT NULL,
[dob] datetime2 NOT NULL,
[email] nvarchar(max) NOT NULL,
[companyDbKey] int NOT NULL,
[GroupDbKey] int NOT NULL,
CONSTRAINT [PK_Employee] PRIMARY KEY ([DbKey]),
CONSTRAINT [FK_Employee_Companies_companyDbKey] FOREIGN KEY ([companyDbKey]) REFERENCES [Companies] ([DbKey]) ON DELETE CASCADE,
CONSTRAINT [FK_Employee_Group_GroupDbKey] FOREIGN KEY ([GroupDbKey]) REFERENCES [Group] ([DbKey]) ON DELETE CASCADE,
CONSTRAINT [FK_Employee_Name_NameDbKey] FOREIGN KEY ([NameDbKey]) REFERENCES [Name] ([DbKey]) ON DELETE CASCADE
);Failed executing DbCommand (4ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
CREATE TABLE [Employee] (
[DbKey] int NOT NULL IDENTITY,
[NameDbKey] int NOT NULL,
[dob] datetime2 NOT NULL,
[email] nvarchar(max) NOT NULL,
[companyDbKey] int NOT NULL,
[GroupDbKey] int NOT NULL,
CONSTRAINT [PK_Employee] PRIMARY KEY ([DbKey]),
CONSTRAINT [FK_Employee_Companies_companyDbKey] FOREIGN KEY ([companyDbKey]) REFERENCES [Companies] ([DbKey]) ON DELETE CASCADE,
CONSTRAINT [FK_Employee_Group_GroupDbKey] FOREIGN KEY ([GroupDbKey]) REFERENCES [Group] ([DbKey]) ON DELETE CASCADE,
CONSTRAINT [FK_Employee_Name_NameDbKey] FOREIGN KEY ([NameDbKey]) REFERENCES [Name] ([DbKey]) ON DELETE CASCADE
);Microsoft.Data.SqlClient.SqlException (0x80131904): Introducing FOREIGN KEY constraint 'FK_Employee_Group_GroupDbKey' on table 'Employee' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
原因分析与解决办法
核心区别:是否存在多重删除路径
AspNetUserRoles可正常运行的原因
AspNetUserRoles是多对多关联表,它的两个外键分别指向完全独立的AspNetUsers和AspNetRoles:- 删除用户时,仅级联删除该用户对应的关联记录
- 删除角色时,仅级联删除该角色对应的关联记录
两条删除路径无交集,不会触发同一记录被多次删除的冲突,SQL Server允许这种配置。
你的Employee表报错的原因
你的实体关系中存在多重删除路径:- 路径1:
Company→Employee,删除Company会直接级联删除关联的Employee - 路径2:
Company→Group→Employee,删除Company会先级联删除关联的Group,再由Group级联删除关联的Employee
同一Employee记录会被两条路径触发删除,SQL Server为避免冲突,抛出循环/多重路径错误。
- 路径1:
解决办法
- 修改级联规则:将其中一条级联删除改为
Restrict或NoAction,比如调整Employee与Group的外键配置:modelBuilder.Entity<Employee>() .HasOne(e => e.Group) .WithMany(g => g.Employees) .HasForeignKey(e => e.GroupID) .OnDelete(ReferentialAction.Restrict); - 调整实体关系:根据业务需求,让Employee仅关联Group,不再直接关联Company,删除Company时通过Group间接删除Employee,避免多重路径。
内容的提问来源于stack exchange,提问作者victorswhisky

