实体框架 ALTER TABLE 语句与 FOREIGN KEY 约束冲突

Posted

技术标签:

【中文标题】实体框架 ALTER TABLE 语句与 FOREIGN KEY 约束冲突【英文标题】:Entity Framework The ALTER TABLE statement conflicted with the FOREIGN KEY constraint 【发布时间】:2014-01-19 05:11:28 【问题描述】:

在更新实体框架中的数据库,代码优先迁移时,我收到此错误:

ALTER TABLE 语句与 FOREIGN KEY 约束“FK_dbo.Clients_dbo.MedicalGroups_MedicalGroupId”冲突。冲突发生在数据库“hrbc”、表“dbo.MedicalGroups”、列“Id”中。

这是我的课:

public partial class Client

    [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id  get; set; 

    public string FirstName  get; set; 
    public string LastName  get; set; 
    public int? MedicalGroupId  get; set; 
    [ForeignKey("MedicalGroupId")]
    public virtual MedicalGroups MedicalGroup  get  return _MedicalGroup;  set  _MedicalGroup = value;  

这是我的第二节课:

public partial class MedicalGroups

    [Key]
    public int Id  get; set; 
    public string Name  get; set; 

这是我正在尝试申请的迁移:

public override void Up()

    AddForeignKey("dbo.Clients", "MedicalGroupId", "dbo.MedicalGroups", "Id");
    CreateIndex("dbo.Clients", "MedicalGroupId");

【问题讨论】:

我认为你需要删除 FK_dbo.Clients_dbo.MedicalGroups_MedicalGroupId 您在这一步之前是否更改了模型,在 Down() 案例中的情况如何?如果你做错了什么,你应该从数据库中删除前索引作为提及@brykneval @brykneval 名称没有问题,因为我确实更改了外键约束的名称但没有帮助!!! @AOZ 是的,我已经在数据库中添加了医疗组表。现在我想在客户和医疗集团之间建立联系。而且down()方法中没有代码 @AOZ 假设我做错了什么我怎么知道? 【参考方案1】:

检查数据库中是否存在与 FK 约束冲突导致创建失败的数据。

【讨论】:

但是MedicalGroupId不是必须的.​​.现有数据应该没有问题 @turnkey 是的,我的客户表中已经有数据,但我无法删除它。 @AOZ 有什么解决办法吗? 这不是删除数据,而是修复它们。如果有Client 引用不存在的MedicalGroup,您将永远无法添加FK。 在我的情况下,我将外键属性设置为 int 而不是 int?所以 EF 添加了一个默认值 0,它指向外部表中没有条目。【参考方案2】:

我认为 @Cory 让您接近正确的解决方案,只是您没有花时间进行调查。

在 add-migration 代码中,可能会生成迁移

public override void Up()

AddColumn("dbo.ClientContacts", "FamilialRelationshipId", c => c.Int(nullable: false));
CreateIndex("dbo.ClientContacts", "FamilialRelationshipId");
AddForeignKey("dbo.ClientContacts", "FamilialRelationshipId", "dbo.FamilialRelationships",        "FamilialRelationshipId");

注意 nullable:false;如果您的模型的 Id 是 int 而不是 int? (nullable int) 迁移代码将 nullable 设置为 false。 您的模型显示您正在使用默认为 0 的不可为空的 int,并且您可能没有值为 0 的外键项。

现在,您必须创建一个存在于外键表中的默认值, 或者如果您使用 SQL Server 创建约束,则不检查创建约束。但请记住这一点:如果您使用 [DefaultValue(0)] 属性装饰您的属性,它不会像 SQL Column Add 那样更改现有数据,如果指定了默认值。

我建议您更改模型类以允许可为空的 int。 然后,在您的种子方法中,针对 dbcontext 创建一个简单的方法,以使用默认值更新新列,因为数据注释中的 [DefaultValue] 属性不会修改您的数据。

Add-Migration / Update-Database 创建列和约束。 接下来,如果您希望允许使用不可为空的 int,请修改您的模型,并假设您再次将所有行更改为外键的某个有效值,即 Add-Migration / Update-database。 这为您的模型迁移提供了一条完整的链。稍后当您发布到实时站点时会派上用场,因为您的数据模型更改流程将保持不变。

希望这会有所帮助。

【讨论】:

我们在这里遇到了两步解决方案的问题。如果必须一次应用多个迁移,您最终会得到以下顺序:创建可为空的字段,使其不可为空,然后运行种子。没有地方可以真正将数据放入 FK 字段,一切都再次爆炸。我找到的唯一两个解决方案是从链接到 FK 的表中删除数据,或者使字段可为空并保持这种状态。 @AriRoth +1 但肯定还有另一种方式,这有多普遍?!【参考方案3】:

这个错误告诉你你违反了外键约束。要解决你有几个解决方案

    修复您的数据 - Clients 表中的某处记录具有 MedicalGroupId,而 MedicalGroups 表。编写查询以找出哪些 ID 没有 存在于MedicalGroups 表中并手动修复数据 自己。 删除外键约束 - 显然,如果您删除外键约束,您将不再被此消息困扰。不幸的是,数据库将不再强制执行这种关系,并且将来可能会使这个问题变得更糟。 使用WITH NOCHECK 创建约束 - 您可以使用WITH NOCHECK 选项创建外键约束。此选项告诉 SQL Server 不要将此约束应用于现有数据。 SQL Server 将在以后的任何 INSERTS/UPDATES/DELETES 中检查此约束。

【讨论】:

【参考方案4】:

对于可能出现在此处的其他人,如果您使用 Code-First Entity Framework 并且只是尝试将新的必需列添加到包含现有数据的表中,则可能会有一个非常简单的解决方法。

注意:在您执行此操作之前,只需知道您需要为所有现有行的此新列添加一个有效值。在继续之前,请考虑您将向现有行添加什么值。您可能希望向所需的查找表提供一个指示“无效”或“未知”的值。

在您的模型中,通过添加?在 int 之后。保存并编译模型。运行更新数据库。

示例:

[ForeignKey("Title")]
public int? TitleId  get; set; 

在数据库中,通过将 NULL 值替换为有效的查找值来更新表。在我的例子中,我将“未知”添加到我的标题查找表中,然后批量编辑所有行以引用该查找 ID。

返回您的模型,删除“?”所以该列不再可以为空。保存并编译模型,然后运行 ​​Update-Database

您现在应该可以开始了。

【讨论】:

【参考方案5】:

出现此问题是因为您的表不是空的。 所以你应该添加新字段而不像外键一样附加它。 public int? MedicalGroupId get; set; .并在包管理控制台中执行update-database 命令。 然后用正确的数据填充此表(客户端)中的字段(值存在于 MedicalGroupsId 中)。 插入创建外键的行

[ForeignKey("MedicalGroupId")]
public virtual MedicalGroups MedicalGroup  get  return _MedicalGroup;  set  _MedicalGroup = value;  

最后执行update-database命令。会好的。

【讨论】:

【参考方案6】:

我得到了我的问题的解决方案。问题是我在客户表中的“数据”。因为我的客户表具有实际不存在的医疗组 ID 值,这就是它在外键约束上给我错误的原因。

Update Client set MedicalGroupId = NULL

【讨论】:

在这种情况下,请将 Cory 或我的答案标记为解决方案,因为这是我们建议检查的内容。【参考方案7】:

我在设置 defaultValue 时也遇到了这个问题,给出:

“对象依赖于列 ALTER TABLE ALTER COLUMN 失败,因为一个或多个对象访问此列”。

最终将 AddForeignKey/DropForeignKey 移动到新的迁移并在单独的更新数据库命令中运行它们(不要在一个命令中执行两个迁移,然后它对我来说失败了。)

    public override void Up()
    
        CreateTable(
            "dbo.DecisionAccesses",
            c => new
            
                Id = c.Int(nullable: false),
                Name = c.String(nullable: false, maxLength: 50),
            )
            .PrimaryKey(t => t.Id);

        CreateTable(
            "dbo.DecisionPersonStatus",
            c => new
            
                Id = c.Int(nullable: false),
                Name = c.String(nullable: false, maxLength: 50),
            )
            .PrimaryKey(t => t.Id);

        AddColumn("dbo.DecisionForm_DecisionFields", "DecisionAccessId", c => c.Int(nullable: false, defaultValue: (int)DecisionAccess.Creator));
        AddColumn("dbo.DecisionMatterPersons", "DecisionPersonStatusId", c => c.Int(nullable: false, defaultValue: (int)DecisionAccess.Creator));
        CreateIndex("dbo.DecisionForm_DecisionFields", "DecisionAccessId");
        CreateIndex("dbo.DecisionMatterPersons", "DecisionPersonStatusId");
        //I moved outcommented to next migration and ran the migrations in separate steps to avoid: (The object is dependent on column ALTER TABLE ALTER COLUMN failed because one or more objects access this column)
        //AddForeignKey("dbo.DecisionForm_DecisionFields", "DecisionAccessId", "dbo.DecisionAccesses", "Id", cascadeDelete: true); 
        //AddForeignKey("dbo.DecisionMatterPersons", "DecisionPersonStatusId", "dbo.DecisionPersonStatus", "Id", cascadeDelete: true);
    


    public override void Down()
    
        //Moved to next migration
        //DropForeignKey("dbo.DecisionMatterPersons", "DecisionPersonStatusId", "dbo.DecisionPersonStatus");
        //DropForeignKey("dbo.DecisionForm_DecisionFields", "DecisionAccessId", "dbo.DecisionAccesses");
    

【讨论】:

【参考方案8】:

假设您的迁移顺序正确,即与外键关联的表将在引用之前创建。请按照以下步骤操作:

    从 SQL 管理工作室备份当前数据库(如果需要) 从 SQL 管理工作室中删除数据库 在 Visual Studio 的“包管理器控制台”中键入“更新数据库”

【讨论】:

【参考方案9】:

如果子表中有孤立记录,也会发生此错误。清理数据库应该可以解决问题。

【讨论】:

【参考方案10】:

在包管理器控制台中运行 Add-Migration InitialCreate –IgnoreChanges 命令。这会创建一个以当前模型作为快照的空迁移。

在包管理器控制台中运行 Update-Database 命令。这会将 InitialCreate 迁移应用到数据库。由于实际的迁移不包含任何更改,它只会在 __MigrationsHistory 表中添加一行,表明该迁移已被应用。

您可以在此处获取更多详细信息:https://msdn.microsoft.com/en-us/library/dn579398(v=vs.113).aspx

【讨论】:

【参考方案11】:

我也收到了这条消息。问题是数据库中的测试数据不正确。 我在测试时没有验证,因此对于不允许空值的字段,我的 ID 为空值。修复数据后,update-database 命令运行成功。

【讨论】:

【参考方案12】:

有这个问题,但幸运的是我只是在建表。我在每张桌子上只有几个样品。删除了两个表中的所有行,然后它就起作用了。我是菜鸟,所以值得一试

【讨论】:

【参考方案13】:

您还可以通过

使用数据播种
modelBuilder.Entity<MedicalGroups>()
.HasData(new MedicalGroups  ... , new MedicalGroups  ... )

在您的 DbContexts 类 OnModelCreating 方法中。

这样,您可以插入引用表中缺少 Id 的所有 MedicalData 行。至少在我的情况下,数据在需要时被播种并且更新数据库正常工作。

【讨论】:

以上是关于实体框架 ALTER TABLE 语句与 FOREIGN KEY 约束冲突的主要内容,如果未能解决你的问题,请参考以下文章

ALTER TABLE 语句与 FOREIGN KEY 约束冲突

如何用oracle里面的alter table语句去添加一个列约束

SQL ALTER TABLE 语句

alter table 如何删除一列,SQL 中的alter 语句用来删除一张表的一列。具体的句法是啥了?

SQL ALTER TABLE MODIFY 语句

SQL-W3School-高级:SQL ALTER TABLE 语句