5

我有一个使用 EF 的 Code First MVC 项目。

有两个表TableATableB,它们目前具有一对多的关系:

public partial class TableA
{ 
    public TableA()
    {
        TableBs = new HashSet<TableB>();
    }

    public int Id { get; set; }
    public Virtual ICollection<TableB> TableBs { get; set; }
}

public partial class TableB
{
    public int Id { get; set; }
    public int TableAId { get; set; }
    public virtual TableA TableAs { get; set; }
}

    modelBuilder.Entity<TableA>()
        .HasMany(e => e.TableBs)
        .WithRequired(e => e.TableA)
        .HasForeignKey(e => e.TableAId);

我试图改变TableA给它一个一对零或一的关系:

public partial class TableA
{ 
    public TableA() { }

    public int Id { get; set; }
    public int? TableBId { get; set; }
    public virtual TableB TableB { get; set; }
}

TableB保持不变,我改变了我的OnModelCreating样子:

    modelBuilder.Entity<TableA>()
        .HasOptional(e => e.TableBs);

    modelBuilder.Entity<TableB>()
        .HasRequired(e => e.TableAs);

问题是生成的迁移是出乎意料的——对我来说是出乎意料的,因为显然我不知道自己在做什么。它不是添加 FK 并添加列,而是尝试重命名列。这是我的迁移:

 public override void Up()
    {
        DropForeignKey("dbo.TableA", "TableBId", "dbo.TableB");
        DropIndex("dbo.TableA", new[] { "TableBId" });
        DropColumn("dbo.TableB", "Id"); 
        RenameColumn(table: "dbo.TableB", name: "TableBId", newName: "Id");
        DropPrimaryKey("dbo.TableB");
        AlterColumn("dbo.TableA", "TableBId", c => c.Int());
        AlterColumn("dbo.TableB", "Id", c => c.Int(nullable: false));
        AlterColumn("dbo.TableB", "TableAId", c => c.Int(nullable: false));
        AddPrimaryKey("dbo.TableB", "Id");
        CreateIndex("dbo.TableB", "Id");
    }

我不明白为什么它试图重命名一个不存在的列,我的新 FK 在哪里?

4

2 回答 2

4

有几件事可能会导致您的问题:

  1. 描述关系的流畅 API 调用

    modelBuilder.Entity<TableA>()
       .HasOptional(e => e.TableBs);
    
    modelBuilder.Entity<TableB>()
       .HasRequired(e => e.TableAs);
    

    因为您在这里使用了两个单独的调用来引用不同的属性,所以我认为 EF 有可能将其解释为两个单独的关系。另一方面,由于它们都是一对一的,所以可能没问题。无论如何,它会更有意义:

    modelBuilder.Entity<TableA>()
       .HasOptional(e => e.TableBs)
       .WithRequired(t => t.TableAs);
    
  2. 实体之间的任何类型的一对一关系都需要两种实体类型共享一个主键(依赖/可选方的主键也是外键),这是生成的迁移一团糟的主要原因。

  3. 我认为您过去也有可能尝试运行迁移,从而TableBIddbo.TableA.

于 2015-07-15T21:57:08.863 回答
0

根据jjj所说我的关系有一个严重的问题,我希望正常迁移可以解决它,但是jjj是正确的,依赖表的FK和PK必须相同。我正在使用在 Azure 上运行的 SQL 服务器,所以我不能只删除列TableB.Id并重命名TableB.TableAId为,TableB.Id因为它给了我一个聚集索引错误。他是正确的,我的 down 语句没有 drop 列,所以我TableBId仍然在dbo.TableA. 我将我的数据库回滚了大约 5 次迁移。我的修订TableA很好:

public partial class TableA
{ 

  public int Id { get; set; }
  public int? TableBId { get; set; }
  public virtual TableB TableB { get; set; }
}

但问题在于TableB我必须TableAId从模型中删除,我在 sudo 代码中添加了列,只是为了使迁移更具可读性:

public partial class TableB
{
  public int Id { get; set; }
  public int Column1 { get; set; }
  public int Column2 { get; set; }
  public int Column3 { get; set; }
  public virtual TableA TableAs { get; set; }
}

现在在我的 DbContext 中,我使用了一个衬垫:

modelBuilder.Entity<TableB>()
            .HasRequired(e => e.TableA).WithOptional(x => x.TableB);

当我添加迁移时,这是新创建的迁移:

     public override void Up()
    {
        DropForeignKey("dbo.TableB", "TableAId", "dbo.TableAId");
        DropIndex("dbo.TableB", new[] { "TableAId" });
        DropColumn("dbo.TableB", "Id");
        RenameColumn(table: "dbo.TableB", name: "TableAId", newName: "Id");
        DropPrimaryKey("dbo.TableB");
        AddColumn("dbo.Tenants", "TableBId", c => c.Int());
        AlterColumn("dbo.TableB", "Id", c => c.Int(nullable: false));
        AlterColumn("dbo.TableB", "Id", c => c.Int(nullable: false));
        AddPrimaryKey("dbo.TableB", "Id");
        CreateIndex("dbo.TableB", "Id");
        AddForeignKey("dbo.TableB", "Id", "dbo.TableA", "Id");
    }

这可能在其他版本的 SQL 中有效,但在 Azure 上无效,因为据我所知,它不允许堆表。所以这就是我更改迁移代码的方式。

public override void Up()
    {
      <-- I am renaming TableB to TableB_Old just for the migration-->
     RenameTable(name: "dbo.TableB", newName: "TableB_Old");
        DropForeignKey("dbo.TableB", "TableAId", "dbo.TableA");
        DropIndex("dbo.TableB_Old", new[] { "TableAId" });
        AlterColumn("dbo.TableB_Old", "TableAId", c => c.Int(nullable: false));
<-- Create a new TableB with the correct layout notice TableAId is not in the column list, so this still matches up to my code model-->
        CreateTable(
           "dbo.TableB",
           c => new
           {
               Id = c.Int(nullable: false)
               Column1 = c.Int(nullable: false)
               Column2 = c.Int(nullable: false)
               Column3 = c.Int(nullable: false)
           })
           .PrimaryKey(t => t.Id)
           .ForeignKey("dbo.TableA", t => t.Id)
           .Index(t => t.Id);

        AddColumn("dbo.TableA", "TableBId", c => c.Int());
<-- This pulls the data from tableB_Old into the newly created TableB, since    TableA had a one-to-many relationship I had to get rid of duplicates this gets  the first record with TableAId and discards the rest. -->

Sql("INSERT INTO [dbo].[TableB] (Id,Column1,Column2,Column3) SELECT     TableAId,,Column1,Column2,Column3 FROM [dbo].[TableB_Old] where [Id] In (select min([Id]) from[dbo].[TableB_Old] as T2 where T2.TableAId = [dbo].[TableB_Old].TableAId)");

<--Now that we pulled all column data we needed from `TableB_Old` we can populate `TableA.TableBId`-->

Sql("UPDATE [TableA] SET [TableBId] = (SELECT [Id] FROM [TableB] WHERE [TableB].[Id] = [TableA].[Id])");

<-- `TableB_Old` was a good table, but it has out lived it's usefullness, it must be removed -->


        DropTable("dbo.TableB_Old");
于 2015-07-17T15:36:04.993 回答