在 1 到 0.1 关系中对对象删除强制实施参照完整性
本文关键字:参照完整性 删除 关系 对象 | 更新日期: 2023-09-27 18:35:44
我创建了一个 asp.NET MVC Web 应用程序,其中包含两个使用 EF Code First 的模型,这两个模型具有 1 到 0。1 关系。
public class ClassA
{
public int Id {get;set;}
//other properties
public virtual ClassB ClassB {get;set;}
}
public class ClassB
{
public int Id {get;set;}
//other properties
}
在我的数据库中,这成功地创建了两个表,其中 A 类具有 B 类的可为空的 FK。这很好用,但删除 ClassA 记录的情况除外。在这种情况下,任何关联的 B 类记录都将保留在数据库中。我知道我可以在删除 POST 方法中手动删除它们:
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public ActionResult DeleteConfirmed(int id)
{
ClassA classA = context.ClassA.Include(c => c.ClassB).First(c => c.Id == id);
context.ClassB.Remove(classA.ClassB);
context.ClassA.Remove(classA);
context.SaveChanges();
}
我不太喜欢这种方法,因为它依赖于我不犯错误,并假设这种方法是删除记录的唯一方法(在某些时候也可能直接对数据库运行 DELETE SQL 语句)。我在这里创建的示例很简单,但我的实际应用程序涉及许多模型和关联,并且变得非常复杂。虽然我喜欢认为我是万无一失的,但我从经验中了解到,我不是,也宁愿不依赖自己来确保记录不会成为孤儿=)
如何强制数据库强制引用完整性,以便在删除类 A 时,在该类 A 中具有外键的任何类 B 也会被删除?
已解决(有点)
正如 Gert 所建议的,我使用 Fluent API 来确保将正确的实体设置为原则,并将所有关系设置为在删除时级联。不过,我确实遇到了一些问题,主要是因为我的数据库中已经有数据。幸运的是,我正处于开发阶段,我可以简单地删除所有数据;否则,我不确定我将如何解决这个问题。
首先,我尝试只添加Fluent API和update-database。我收到一个错误,部分内容如下:"参数@objname不明确或声明的@objtype(COLUMN)错误",这似乎是由于 EF 试图更改现有 FK 列的名称。在这种情况下,我决定使用 2 个迁移:一个用于删除现有关系,另一个用于添加新的重新配置的关系。我不得不在一系列相当具体的事件中这样做。
- 注释掉了控制器中对受影响关系的所有引用,以避免更新时出错。
注释掉模型中的关系。
public class ClassA { public int Id {get;set;} //other properties //public virtual ClassB ClassB {get;set;} }
- 添加迁移和更新数据库以删除现有关系。
- 通过取消注释我在步骤 1 和 2 中注释掉的所有内容来撤消对模型和控制器的所有更改。
按照 Gert 的建议在 OnModelCreate 中配置新关系以保存新关系。
modelBuilder.Entity<ClassA>() .HasOptional(b => b.ClassB) .WithRequired() .Map(m => m.MapKey("ClassB_Id")) .WillCascadeOnDelete();
添加迁移和更新数据库以创建新关系。当我的数据库中存在现有数据时,此步骤失败。如果我没有简单地清除数据库中所有数据的选项,我不确定我将如何完成此操作。
在一对一的关联中,您始终必须考虑哪个实体是主要实体,哪个是依赖实体。正如这个词所暗示的那样,依赖者不能没有另一个而存在。
在你的设计中,原则和依赖是错误的方式:ClassA
是依赖者,ClassB
可以自己生活。这就是 EF 恰好解释类模型的方式。如果您希望它以其他方式执行此操作,则必须添加一些映射说明。保持类模型不变,这只能由流畅的 API 完成,例如在上下文的OnModelCreating
覆盖中:
modelBuilder.Entity<ClassA>().HasOptional(a => a.ClassB)
.WithRequired().Map(m => m.MapKey("ClassAId"))
.WillCascadeOnDelete();
现在ClassB
表中将有一个外键 ClassAId
。级联删除可确保在删除ClassA
时,自动删除其从属ClassB
。
将 SQL Server 数据库中的外键约束更改为"删除级联时"
https://stackoverflow.com/a/6260736/1056639
ALTER TABLE dbo.T2
DROP CONSTRAINT FK_T1_T2 -- or whatever it's called
ALTER TABLE dbo.T2
ADD CONSTRAINT FK_T1_T2_Cascade
FOREIGN KEY (EmployeeID) REFERENCES dbo.T1(EmployeeID) ON DELETE CASCADE