在添加迁移后尝试更新数据库时出错。
以下是添加迁移之前的类
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'
如何解决此问题并使我的迁移恢复正常?
答案 0 :(得分:39)
解决此问题的关键是将迁移分为两次迁移。首先,添加一个可空字段并填写数据。其次,使该字段成为必需的外键。
将新属性作为可空类型(例如int?)
添加到您的类中public class MyOtherEntity
{
public int Id { get; set; }
}
public class MyEntity
{
...
// New reference to MyOtherEntity
public int? MyOtherEntityId { get; set; }
...
}
创建迁移。注意:迁移名称并不重要,但“AddBlogPosts1”之类的内容使其易于阅读。
> add-migration AddMyEntityMyOtherEntity1
这应该支持一个如下所示的迁移:
public partial class AddMyTableNewProperty1 : DbMigration
{
public override void Up()
{
AddColumn("dbo.MyEntity", "MyOtherEntityId", c => c.Int());
}
public override void Down()
{
DropColumn("dbo.MyEntity", "MyOtherEntityId");
}
}
现在手动编辑生成的迁移,为新字段添加默认值。最简单的情况是默认值是不变的。如果需要,您可以在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");
}
}
更新数据库
> update-database
返回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; }
...
}
创建另一个迁移
> add-migration AddMyEntityMyOtherEntity2
这应该创建如下的迁移:
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());
}
}
更新数据库
> update-database
答案 1 :(得分: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)
当您尝试在已包含数据的表的非可空列上添加外键约束时,可能会发生这种情况。如果您的表包含数据,请先尝试删除它们,然后重新尝试更新数据库。
答案 3 :(得分:2)
快速回答是 - 首先为ProductId添加一个可为空的列,然后在所有现有行中设置一些默认值,然后将列设置为非null(如果需要)以供将来插入,添加最后添加外键约束。
我写了一篇很长篇博文,其中包含完整的源代码 - http://nodogmablog.bryanhogan.net/2015/04/entity-framework-non-null-foreign-key-migration/
答案 4 :(得分:1)
这是一个老问题,但目前无需创建单独的迁移,只需几个步骤即可解决此问题:
在上面的例子中,这将是:
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");
}
答案 5 :(得分:1)
我不确定这个主题是否仍然存在,但是至少使用EF 6,您可以执行以下操作将默认值分配给新的外键。
这是EF默认情况下如何创建新外键的方式:
AddColumn("dbo.Table", "AnotherTableId", c => .Int(nullable: false));
CreateIndex("dbo.Table", "AnotherTableId");
AddForeignKey("dbo.Table", "AnotherTableId", "dbo.AnotherTable", "Id");
只需进行如下更改
AddColumn("dbo.Table", "AnotherTableId", c =>
{
var model = c.Int(nullable: false);
model.DefaultValue = 1;
return model;
});
CreateIndex("dbo.Table", "AnotherTableId");
AddForeignKey("dbo.Table", "AnotherTableId", "dbo.AnotherTable", "Id");