【问题标题】:How to add a new Foreign Key from a new table to existing records如何将新表中的新外键添加到现有记录
【发布时间】:2018-03-12 18:08:09
【问题描述】:

使用 C# MVC 和实体框架 v6.2.0

我有一个现有的模型,Caller

public class Caller
{
    [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public Guid Id { get; set; }

    [Required]
    public string FirstName { get; set; }
}

我创建了一个新模型,LanguageChoice

public class LanguageChoice
{
    [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public Guid Id { get; set; }

    [Required]
    public string Name { get; set; }
}

我想将这个新的LanguageChoice 添加到Caller

public class Caller
{
    [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public Guid Id { get; set; }

    [Required]
    public string FirstName { get; set; }

    public Guid LanguageChoice_Id { get; set; } // <----

    [ForeignKey("LanguageChoice_Id")]
    public LanguageChoice LanguageChoice { get; set; } // <----
}

我这样做是因为它将在我的 ViewModel 中成为 IEnumerable&lt;SelectListItem&gt;

在我的数据库中,我已有 Caller 记录:

╔═════════════════════╦═══════════╗
║         Id          ║ FirstName ║
╠═════════════════════╬═══════════╣
║ caller_guid_value_1 ║ Ryan      ║
║ caller_guid_value_2 ║ John      ║
╚═════════════════════╩═══════════╝

当我添加LanguageChoice 时,预期的结果将是:

╔═════════════════════╦═══════════╦═════════════════════════════╗
║         Id          ║ FirstName ║      LanguageChoice_Id      ║
╠═════════════════════╬═══════════╬═════════════════════════════╣
║ caller_guid_value_1 ║ Ryan      ║ languageChoice_guid_value_1 ║
║ caller_guid_value_2 ║ John      ║ languageChoice_guid_value_1 ║
╚═════════════════════╩═══════════╩═════════════════════════════╝

这是我的迁移:

public partial class AddCallerLanguageChoice : DbMigration
{
    public override void Up()
    {
        CreateTable(
            "dbo.LanguageChoices",
            c => new
                {
                    Id = c.Guid(nullable: false, identity: true),
                    Name = c.String(nullable: false, maxLength: 50),
                })
            .PrimaryKey(t => t.Id);

        AddColumn("dbo.Callers", "LanguageChoice_Id", c => c.Guid(nullable: false));
        CreateIndex("dbo.Callers", "LanguageChoice_Id");
        AddForeignKey("dbo.Callers", "LanguageChoice_Id", "dbo.LanguageChoices", "Id", cascadeDelete: true);
    }

    public override void Down()
    {
        DropForeignKey("dbo.Callers", "LanguageChoice_Id", "dbo.LanguageChoices");
        DropIndex("dbo.Callers", new[] { "LanguageChoice_Id" });
        DropColumn("dbo.Callers", "LanguageChoice_Id");
        DropTable("dbo.LanguageChoices");
    }
}

这是我的错误:

ALTER TABLE 语句与 FOREIGN KEY 约束“FK_dbo.Callers_dbo.LanguageChoices_LanguageChoice_Id”冲突。冲突发生在数据库“aspnet-My Web App-20171030040324”、表“dbo.LanguageChoices”、“Id”列中。

有没有办法做到这一点,还是我应该继续允许LanguageChoice_Id 可以为空?


4年前的类似问题:Entity Framework The ALTER TABLE statement conflicted with the FOREIGN KEY constraint
  • 4 岁,也许从那以后发生了一些变化
  • 他们使用 int 值作为 Id,我使用 Guid,所以我无法提前猜测/假设 Id 值
  • 我想避免创建一个迁移,其中LanguageChoice_Id 可以为空,然后是更新记录的种子方法,然后是另一个使LanguageChoice_Id 不可为空的迁移,因为在将两个迁移都推送到 Azure 时会中断种子方法将最后运行。 (参见 Ari Roth 对 RicklsWright 回答的评论)


更新:

按照@henoc salinas'更新的答案,我来到了这个可行的迁移文件。

Henoc 的答案是公认的答案,这只是为了展示我最终得到的结果,不同之处在于我在此处插入了表值并使用非临时值更新了 Caller 的空值:

        public override void Up()
    {
        CreateTable(
            "dbo.LanguageChoices",
            c => new
                {
                    Id = c.Guid(nullable: false, identity: true),
                    Name = c.String(nullable: false, maxLength: 50),
                })
            .PrimaryKey(t => t.Id);

        Sql(String.Format("INSERT INTO LanguageChoices VALUES ('{0}','{1}')", Guid.NewGuid(), "English"));
        Sql(String.Format("INSERT INTO LanguageChoices VALUES ('{0}','{1}')", Guid.NewGuid(), "Other"));
        Sql(String.Format("INSERT INTO LanguageChoices VALUES ('{0}','{1}')", Guid.NewGuid(), "Spanish"));

        AddColumn("dbo.Callers", "LanguageChoice_Id", c => c.Guid(nullable: true));

        Sql("UPDATE Callers Set LanguageChoice_Id = (SELECT Id FROM LanguageChoices WHERE Name = \'English\') WHERE LanguageChoice_Id IS NULL");

        AlterColumn("dbo.Callers", "LanguageChoice_Id", c => c.Guid(nullable: false));

        CreateIndex("dbo.Callers", "LanguageChoice_Id");
        AddForeignKey("dbo.Callers", "LanguageChoice_Id", "dbo.LanguageChoices", "Id", cascadeDelete: true);
    }

    public override void Down()
    {
        DropForeignKey("dbo.Callers", "LanguageChoice_Id", "dbo.LanguageChoices");
        DropIndex("dbo.Callers", new[] { "LanguageChoice_Id" });
        DropColumn("dbo.Callers", "LanguageChoice_Id");
        DropTable("dbo.LanguageChoices");
    }
}

【问题讨论】:

  • 到目前为止一切都没有改变(很遗憾)。
  • @IvanStoev 真可惜。

标签: c# entity-framework tsql


【解决方案1】:

产生这个冲突是因为新表LanguageChoices在创建时没有元素,所以外键因为不可为空而没有找到有效值,你可以试试改变:

 public Guid LanguageChoice_Id { get; set; }

到这里:

  public Guid? LanguageChoice_Id { get; set; }

然后再次生成迁移,你可以看到这一行:

 AddColumn("dbo.Callers", "LanguageChoice_Id", c => c.Guid(nullable: false));

改成这样:

 AddColumn("dbo.Callers", "LanguageChoice_Id", c => c.Guid());

现在,您可以更新数据库,并将列更改为不为空,添加有效值并再次更改该行,然后生成新的迁移:

public Guid? LanguageChoice_Id { get; set; }

到:

public Guid LanguageChoice_Id { get; set; }

在您发表评论之后

在标记上插入 sql 代码:

public partial class AddCallerLanguageChoice : DbMigration
{
    public override void Up()
    {
        CreateTable(
            "dbo.LanguageChoices",
            c => new
                {
                    Id = c.Guid(nullable: false, identity: true),
                    Name = c.String(nullable: false, maxLength: 50),
                })
            .PrimaryKey(t => t.Id);

        string TempKey = Guid.NewGuid ( ).ToString();
        this.Sql ( String.Format ( "Insert into dbo.LanguageChoices values ({0},{1})", TempKey, "Initial" ) );

        AddColumn("dbo.Callers", "LanguageChoice_Id", c => c.Guid(nullable: false));

        Sql ( String.Format ( "Update dbo.Callers set LanguageChoice_Id='{0}' where LanguageChoice_Id is null", TempKey ) );

        CreateIndex("dbo.Callers", "LanguageChoice_Id");
        AddForeignKey("dbo.Callers", "LanguageChoice_Id", "dbo.LanguageChoices", "Id", cascadeDelete: true);
    }

    public override void Down()
    {
        DropForeignKey("dbo.Callers", "LanguageChoice_Id", "dbo.LanguageChoices");
        DropIndex("dbo.Callers", new[] { "LanguageChoice_Id" });
        DropColumn("dbo.Callers", "LanguageChoice_Id");
        DropTable("dbo.LanguageChoices");
    }
}

【讨论】:

  • 这遇到了我在帖子底部提到的问题。我将一次将两个迁移推送到不同的服务器(我将)我会收到一个错误,因为在 EF 尝试将它们更新为不可为空的字段之前我无法更改它们。跨度>
  • 在迁移和新表中创建列后,是否尝试插入sql代码给新列赋值?
  • 您可以在架构更改迁移之间的中间迁移方法中直接包含 SQL 以更新字段,Sql("UPDATE .....");
  • 使用你的建议,@BradleyUffner,我在它自己的迁移中有这个声明:string sqlStatement = "UPDATE Callers SET LanguageChoice_Id = \'" + languageChoice.Id + "\' WHERE LanguageChoice_Id IS NULL"; Sql(sqlStatement); 后跟一个迁移,使LanguageChoice_Id 不可为空,这在一次执行一个迁移时有效,但不是一次全部。我必须使用ApplicationDbContext 来获取Id 的值,所以在迁移过程中会报错说ApplicationDbContext 已经改变。
  • 我编辑了我的答案,请在 cmets 之后再次查看
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-02-17
  • 2013-07-13
  • 1970-01-01
  • 1970-01-01
  • 2012-04-19
  • 2014-02-20
相关资源
最近更新 更多