使用Code First迁移添加外键

问题描述 投票:17回答:5

添加迁移后尝试更新数据库时出错。

以下是添加迁移之前的类

public class Product
{
    public Product() { }

    public int ProductId { get; set; } 
    public string Name { get; set; }
    public decimal Price { get; set; }
    public bool Istaxable { get; set; }
    public string DefaultImage { get; set; }
    public IList<Feature> Features { get; set; }
    public IList<Descriptor> Descriptors { get; set; }
    public IList<Image> Images { get; set; }
    public IList<Category> Categories { get; set; }
}


public class Feature
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string Description { get; set; }

}

现在我想在Feature类中添加一个外键并以这种方式重构类:

public class Product
{
    public Product() { }

    public int ProductId { get; set; } 
    public string Name { get; set; }
    public decimal Price { get; set; }
    public bool Istaxable { get; set; }
    public string DefaultImage { get; set; }
    public IList<Feature> Features { get; set; }
    public IList<Descriptor> Descriptors { get; set; }
    public IList<Image> Images { get; set; }
    public IList<Category> Categories { get; set; }
}

public class Feature
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string Description { get; set; }
    public string Image { get; set; }
    public string VideoLink { get; set; }

    public int ProductId { get; set; }
    public Product Product { get; set; }
}

我用Add-Migration命令添加了一个迁移。我添加了一个Update-Database命令,这是我得到的:

ALTER TABLE语句与FOREIGN KEY约束“FK_dbo.ProductFeatures_dbo.Products_ProductId”冲突。冲突发生在数据库“CBL”,表“dbo.Products”,列'ProductId'

我该怎么做才能解决这个问题,让我的迁移恢复正常?

entity-framework foreign-keys ef-migrations
5个回答
39
投票

解决此问题的关键是将迁移分为两次迁移。首先,添加一个可空字段并填写数据。其次,使该字段成为必需的外键。

第一次迁移

  1. 将新属性作为可空类型添加到您的类中(例如int?) public class MyOtherEntity { public int Id { get; set; } } public class MyEntity { ... // New reference to MyOtherEntity public int? MyOtherEntityId { get; set; } ... }
  2. 创建迁移。注意:迁移名称并不重要,但“AddBlogPosts1”之类的内容使其易于阅读。 > add-migration AddMyEntityMyOtherEntity1
  3. 这应该支持一个如下所示的迁移: public partial class AddMyTableNewProperty1 : DbMigration { public override void Up() { AddColumn("dbo.MyEntity", "MyOtherEntityId", c => c.Int()); } public override void Down() { DropColumn("dbo.MyEntity", "MyOtherEntityId"); } }
  4. 现在,手动编辑生成的迁移,为新字段添加默认值。最简单的情况是默认值是不变的。如果需要,您可以在SQL中添加更多逻辑。此示例假定所有MyEntity实例都指向ID为1的同一MyOtherEntity实例。 public partial class AddMyTableNewProperty1 : DbMigration { public override void Up() { AddColumn("dbo.MyEntity", "MyOtherEntityId", c => c.Int()); // ADD THIS BY HAND Sql(@"UPDATE dbo.MyEntity SET MyOtherEntityId = 1 where MyOtherEntity IS NULL"); } public override void Down() { DropColumn("dbo.MyEntity", "MyOtherEntityId"); } }
  5. 更新数据库 > update-database

第二次迁移

  1. 返回到MyEntity类并更改新属性以表示强制外键。 public class MyEntity { ... // Change the int? to int to make it mandatory public int MyOtherEntityId { get; set; } // Create a reference to the other entity public virtual MyOtherEntity MyOtherEntity { get; set; } ... }
  2. 创建另一个迁移 > add-migration AddMyEntityMyOtherEntity2
  3. 这应该创建如下的迁移: public partial class AddMyEntityMyOtherEntity2: DbMigration { public override void Up() { AlterColumn("dbo.MyEntity", "MyOtherEntityId", c => c.Int(nullable: false)); CreateIndex("dbo.MyEntity", "MyOtherEntityId"); AddForeignKey("dbo.MyEntity", "MyOtherEntityId", "dbo.MyOtherEntity", "Id"); } public override void Down() { DropForeignKey("dbo.MyEntity", "MyOtherEntityId", "dbo.MyOtherEntity"); DropIndex("dbo.MyEntity", new[] { "MyOtherEntityId" }); AlterColumn("dbo.MyEntity", "MyOtherEntityId", c => c.Int()); } }
  4. 更新数据库 > update-database

其他说明

  1. 此技术适用于在应用程序启动期间应用的迁移。
  2. 可以在SQL中为新列添加更复杂的映射,但这里没有说明。

6
投票

我今天遇到了这个问题。这就是我处理它的方式。我确实必须让我的FK属性可以为空,这在我的案例中并不是什么大问题。

我对POCO进行了更改,生成了迁移,然后将以下内容添加到迁移中。

public partial class LineItemProductionHistory_v4 : DbMigration
{
    public override void Up()
    {
        AddColumn("LineItemProductionHistories","TempLineItemId",c=>c.Int());
        Sql("Update LineItemProductionHistories Set TempLineItemId = LineItem_Id");
       .
       . Generated code
       .
        Sql("Update LineItemProductionHistories Set LineItemId = TempLineItemId");
        DropColumn("LineItemProductionHistories", "TempLineItemId");
    }
}

我只是暂时存储所有外键,让迁移执行添加/删除操作,然后将外键放回新创建的FK中。


2
投票

当您尝试在已包含数据的表的非可空列上添加外键约束时,可能会发生此问题。如果您的表包含数据,请先尝试删除它们,然后重试更新数据库。


2
投票

快速回答是 - 首先为ProductId添加一个可为空的列,然后在所有现有行中设置一些默认值,然后将列设置为非null(如果需要)以供将来插入,添加最后添加外键约束。

我写了一篇很长的博客文章,其中包含完整的源代码 - http://nodogmablog.bryanhogan.net/2015/04/entity-framework-non-null-foreign-key-migration/


1
投票

这是一个老问题,但目前无需创建单独的迁移,只需几个步骤即可解决此问题:

  1. 使用实体更改运行添加迁移(添加了新的非可空引用属性,在本例中为ProductId)以构建新的迁移类
  2. 修改新添加的迁移以创建可为空的列(可为空:true)而不是非可空的
  3. 在AddColumn下面添加Sql命令,根据需要设置列的值
  4. 下面添加AlterColumn命令,该命令将使列不可为空

在上面的示例中,这将看起来像:

    public override void Up()
    {
        AddColumn("dbo.ProductFeatures", "ProductId", c => c.Int(nullable: true));
        Sql("UPDATE [dbo].[ProductFeatures] SET ProductId = (SELECT TOP 1 [Id] FROM [dbo].[Products])");
        AlterColumn("dbo.ProductFeatures", "ProductId", c => c.Int(nullable: false));
        CreateIndex("dbo.ProductFeatures", "ProductId");
        AddForeignKey("dbo.ProductFeatures", "ProductId", "dbo.Products", "Id");
    }
© www.soinside.com 2019 - 2024. All rights reserved.