在1到0..1关系中强制执行对象删除的引用完整性

时间:2014-12-31 20:45:10

标签: c# asp.net-mvc entity-framework relationships

我创建了一个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
}

在我的数据库中,这成功创建了两个表,ClassA具有ClassB的Nullable FK。除了删除ClassA记录的情况之外,这很有用。在这种情况下,任何关联的ClassB记录都保留在数据库中。我知道我可以在Delete 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语句)在某些方面)。我在这里创建的例子很简单,但我的实际应用程序涉及很多模型和关联,而且它变得非常复杂。虽然我喜欢认为我是绝对正确的,但我从经验中学到了我不是并且宁愿不依靠自己来确保记录不会成为孤儿=)

如何强制数据库强制引用完整性,以便在删除ClassA时,还删除在该ClassA中具有外键的任何ClassB?

已解决(有点)

正如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的建议在OnModelCreating中配置新关系以保存新关系。

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

2 个答案:

答案 0 :(得分:4)

在一对一关联中,您始终必须考虑哪个实体是原则实体,哪个实体是依赖实体。正如文字暗示的那样,依赖者不能没有另一个人。

在你的设计中,原则和依赖是错误的方式:ClassA是依赖,ClassB可以独立生存。这就是EF碰巧解释类模型的方式。如果你想要它,否则你必须添加一些映射指令。保持类模型不变,这只能通过流畅的API完成,例如在上下文的OnModelCreating覆盖中:

modelBuilder.Entity<ClassA>().HasOptional(a => a.ClassB)
    .WithRequired().Map(m => m.MapKey("ClassAId"))
    .WillCascadeOnDelete();

现在ClassAId表中会有一个外键ClassB。级联删除可确保删除ClassA后,其相关ClassB会自动删除。

答案 1 :(得分:0)

将sql server数据库中的外键约束更改为“ON DELETE CASCADE”

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