EF 代码先用空查找表设置外键

Posted

技术标签:

【中文标题】EF 代码先用空查找表设置外键【英文标题】:EF Code first set a foreign key with an empty lookup table 【发布时间】:2014-04-07 10:59:28 【问题描述】:

我遇到了与查找表和外键相关的 EF 代码优先迁移问题。假设我的代码中有这两个类:

public class Test

    [Key]
    public long Id  get; set; 

    [Required]
    public string Title  get; set; 

    [Required, DisplayName("Test type")]
    public TestType TestType  get; set; 


public class TestType

    public int Id  get; set; 
    public string Name  get; set; 

TestType 是一个典型的查找表,我通常用Seed() 方法填充它们:

context.TestTypes.AddOrUpdate(
    it => it.Name,
    new TestType()  Name = "Drug" ,
    new TestType()  Name = "Educational" ,
    new TestType()  Name = "Other" 
);

当我创建具有关系的表时,我得到以下迁移:

CreateTable(
    "dbo.TestTypes",
    c => new
        
            Id = c.Int(nullable: false, identity: true),
            Name = c.String(),
        )
    .PrimaryKey(t => t.Id);

AddColumn("dbo.Tests", "TestType_Id", c => c.Int(nullable: false));
CreateIndex("dbo.Tests", "TestType_Id");
AddForeignKey("dbo.Tests", "TestType_Id", "dbo.TestTypes", "Id", cascadeDelete: true);

现在,如果我执行迁移,我当然会收到一个错误,因为鉴于查找表仍然为空并且创建的列没有默认值,因此无法尊重外键。

开发中,我可以通过简单地创建两个迁移来解决这个问题,第一个用于创建查找表,第二个用于设置外键。如果我单独运行它们,那么第一个之后的 Seed 方法将填充表,我可以调整列创建以从数据库中获取值以在创建外键之前预填充列,有点像这样:

AddColumn("dbo.Tests", "TestType_Id", c => c.Int(nullable: false));
Sql("UPDATE dbo.Tests SET TestType_Id = (SELECT TOP 1 Id FROM dbo.TestTypes)");
CreateIndex("dbo.Tests", "TestType_Id");
AddForeignKey("dbo.Tests", "TestType_Id", "dbo.TestTypes", "Id", cascadeDelete: true);

然后当我运行它时,一切正常。

现在,在 PRODUCTION 中我没有同样的奢侈,因为所有迁移都是在运行 Seed 方法之前运行的,所以我总是会遇到同样的问题。

我知道我也可以可能在生产数据库上按步进顺序运行迁移,但这并不能真正解决问题...假设我的一位同事更新了他的工作副本并运行迁移,一切都会按顺序运行,他肯定会遇到错误。

【问题讨论】:

TestType 也不应该创建表吗? @Tallmaris 你有没有找到一个好的解决方案? 并非如此。我基本上可以选择为新列设置nullable:true,然后在将来进行调整(这并不理想),或者只是将种子添加到迁移本身,因为它们彼此紧密相连。 【参考方案1】:

我不确定你的数据库的当前状态,但我会这样定义你的模型

public class Test

    [Key]
    public long Id  get; set; 

    [Required]
    public string Title  get; set; 

    [Required]
    [ForeignKey("TestType")]
    public int TestTypeId  get; set; 

    [DisplayName("Test type")]
    public virtual TestType TestType  get; set; 


public class TestType

    [Key]
    public int Id  get; set; 

    public string Name  get; set; 

当表不存在时,这会导致以下迁移。我总是发现明确描述外键效果更好。

public override void Up()

    CreateTable(
        "dbo.Tests",
        c => new
            
                Id = c.Long(nullable: false, identity: true),
                Title = c.String(nullable: false),
                TestTypeId = c.Int(nullable: false),
            )
        .PrimaryKey(t => t.Id)
        .ForeignKey("dbo.TestTypes", t => t.TestTypeId)
        .Index(t => t.TestTypeId);

    CreateTable(
        "dbo.TestTypes",
        c => new
            
                Id = c.Int(nullable: false, identity: true),
                Name = c.String(),
            )
        .PrimaryKey(t => t.Id);

只要测试表为空,种子就应该可以正常工作?

【讨论】:

是的,问题是Test 表不为空...是的,我使用相同的设置外键的方法(我不喜欢下划线默认值)但上面只是示例代码。

以上是关于EF 代码先用空查找表设置外键的主要内容,如果未能解决你的问题,请参考以下文章

ASP.NET EF实体主外键关系

外键约束,带有子对象集合的 EF

在MySQL中如何设置主键和外键

EF之Code First设置主外键关系

csharp EF Core设置所有外键以删除restrict

EF实体类,设置导航属性,导致序列化时出现"循环引用"错误,及序列化时间格式解决方案