当前位置: 首页 > 工具软件 > Fist > 使用案例 >

EF CodeFist 多重外键约束

司徒墨竹
2023-12-01
对于多重外键的概念,请参考 Sql 多重外键约束 一章


以一个部门类定义有中正副两个管理者的情况为例

//部门类定义
public class Department
{
	public int ID { get; set; }

	public string Name { get; set; }

	public int PrimaryManagerID { get; set; } //正管理者外键

	public virtual Manager PrimaryManager { get; set; } //正管理者导航

	public int SecondManagerID { get; set; } //副管理者外键

	public virtual Manager SecondManager { get; set; } //副管理者导航
}

//管理者类定义
public class Manager
{
	public int ID { get; set; }

	public string Name { get; set; }

	public virtual ICollection<Department> PrimaryDepartments { get; set; } //正职部门导航

	public virtual ICollection<Department> SecondDepartments { get; set; } //副职部门导航
}


这样的定义会使EF产生一个异常:
将 FOREIGN KEY 约束 'FK_dbo.Department_dbo.Manager_SecondManagerID' 引入表 'Department' 可能会导致循环或多重级联路径。请指定 ON DELETE NO ACTION 或 ON UPDATE NO ACTION,或修改其他 FOREIGN KEY 约束。

因为默认情况下CodeFirst会在外键约束中设置 删除规则 为级联(不会默认设置 更新规则 为级联),在多重外键情况下,所有外键约束都被设置了级联删除,而这在Sql Server中是不允许的,解决办法是使用Fluent API 去掉外键约束中的级联删除规则(最多可保留一个)

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
	modelBuilder.Entity<Department>().HasRequired(m => m.PrimaryManager).WithMany(n => n.PrimaryDepartments).HasForeignKey(m => m.PrimaryManagerID).WillCascadeOnDelete(false);
	modelBuilder.Entity<Department>().HasRequired(m => m.SecondManager).WithMany(n => n.SecondDepartments).HasForeignKey(m => m.SecondManagerID).WillCascadeOnDelete(false);

	modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
	
	base.OnModelCreating(modelBuilder);
}


再看另一种正副管理者的定义形式

//部门类定义
public class Department
{
	public int ID { get; set; }

	public string Name { get; set; }

	public virtual Manager PrimaryManager { get; set; } //正管理者导航

	public virtual Manager SecondManager { get; set; } //副管理者导航
}

//管理者类定义
public class Manager
{
	public int ID { get; set; }

	public string Name { get; set; }

	public virtual ICollection<Department> PrimaryDepartments { get; set; } //正职部门导航

	public virtual ICollection<Department> SecondDepartments { get; set; } //副职部门导航
}

CodeFirst中当仅定义了导航属性,而没有显示定义外键时,不会设置 删除规则 为级联,于是就不会发生上面的异常,生成的部门表结构如下,可以注意到,两个外键约束中没有了ON DELETE CASCADE

CREATE TABLE [dbo].[Department] --部门表
(
[ID] [int] NOT NULL IDENTITY(1, 1),
[Name] [nvarchar] (max) COLLATE Chinese_PRC_CI_AS NULL,
[Manager_ID] [int] NULL,
[Manager_ID1] [int] NULL,
[PrimaryManager_ID] [int] NULL,
[SecondManager_ID] [int] NULL
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
ALTER TABLE [dbo].[Department] ADD CONSTRAINT [PK_dbo.Department] PRIMARY KEY CLUSTERED  ([ID]) ON [PRIMARY]
GO
CREATE NONCLUSTERED INDEX [IX_Manager_ID] ON [dbo].[Department] ([Manager_ID]) ON [PRIMARY]
GO
CREATE NONCLUSTERED INDEX [IX_Manager_ID1] ON [dbo].[Department] ([Manager_ID1]) ON [PRIMARY]
GO
CREATE NONCLUSTERED INDEX [IX_PrimaryManager_ID] ON [dbo].[Department] ([PrimaryManager_ID]) ON [PRIMARY]
GO
CREATE NONCLUSTERED INDEX [IX_SecondManager_ID] ON [dbo].[Department] ([SecondManager_ID]) ON [PRIMARY]
GO
ALTER TABLE [dbo].[Department] ADD CONSTRAINT [FK_dbo.Department_dbo.Manager_Manager_ID] FOREIGN KEY ([Manager_ID]) REFERENCES [dbo].[Manager] ([ID])
GO
ALTER TABLE [dbo].[Department] ADD CONSTRAINT [FK_dbo.Department_dbo.Manager_Manager_ID1] FOREIGN KEY ([Manager_ID1]) REFERENCES [dbo].[Manager] ([ID])
GO
ALTER TABLE [dbo].[Department] ADD CONSTRAINT [FK_dbo.Department_dbo.Manager_PrimaryManager_ID] FOREIGN KEY ([PrimaryManager_ID]) REFERENCES [dbo].[Manager] ([ID])
GO
ALTER TABLE [dbo].[Department] ADD CONSTRAINT [FK_dbo.Department_dbo.Manager_SecondManager_ID] FOREIGN KEY ([SecondManager_ID]) REFERENCES [dbo].[Manager] ([ID])
GO


但由于Department没有显示定义外键,且包含多重外键,所以导致CodeFirst因无法明确关系,使表中包含了四个而不是两个外键,解决办法就是使用InverseProperty(逆向属性)特性,它存在于System.ComponentModel.DataAnnotations.Schema命名空间,InverseProperty既然可以用在Department类,也可以用在Manager类,或两边都用


//部门类定义
public class Department
{
	public int ID { get; set; }

	public string Name { get; set; }

	[InverseProperty("PrimaryDepartments")]
	public virtual Manager PrimaryManager { get; set; } //正管理者导航

	[InverseProperty("SecondDepartments")]
	public virtual Manager SecondManager { get; set; } //副管理者导航
}

//管理者类定义
public class Manager
{
	public int ID { get; set; }

	public string Name { get; set; }

	[InverseProperty("PrimaryManager")]
	public virtual ICollection<Department> PrimaryDepartments { get; set; } //正职部门导航

	[InverseProperty("SecondManager")]
	public virtual ICollection<Department> SecondDepartments { get; set; } //副职部门导航
}




 类似资料: