在 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 个迁移:一个用于删除现有关系,另一个用于添加新的重新配置的关系。我不得不在一系列相当具体的事件中这样做。

  1. 注释掉了控制器中对受影响关系的所有引用,以避免更新时出错。
  2. 注释掉模型中的关系。

    public class ClassA
    {
        public int Id {get;set;}
        //other properties
        //public virtual ClassB ClassB {get;set;}
    }
    
  3. 添加迁移和更新数据库以删除现有关系。
  4. 通过取消注释我在步骤 1 和 2 中注释掉的所有内容来撤消对模型和控制器的所有更改。
  5. 按照 Gert 的建议在 OnModelCreate 中配置新关系以保存新关系。

    modelBuilder.Entity<ClassA>()
        .HasOptional(b => b.ClassB)
        .WithRequired()
        .Map(m => m.MapKey("ClassB_Id"))
        .WillCascadeOnDelete();
    
  6. 添加迁移和更新数据库以创建新关系。当我的数据库中存在现有数据时,此步骤失败。如果我没有简单地清除数据库中所有数据的选项,我不确定我将如何完成此操作。

在 1 到 0.1 关系中对对象删除强制实施参照完整性

在一对一的关联中,您始终必须考虑哪个实体是主要实体,哪个是依赖实体。正如这个词所暗示的那样,依赖者不能没有另一个而存在。

在你的设计中,原则和依赖是错误的方式: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