30

我使用 Entity Framework 4.3 Code First 和手动迁移和 SQL Express 2008 开始了一个项目,最近更新到 EF5(在 VS 2010 中),并注意到现在当我更改外键约束之类的东西时,迁移代码会添加“dbo”。到表名的开头,因此它构造的外键名对于现有约束是不正确的(现在通常看起来很奇怪)。

EF 4.3 中的原始迁移脚本(注意ForeignKey("Products", t => t.Product_Id)):

    CreateTable(
        "Products",
        c => new
            {
                Id = c.Int(nullable: false, identity: true),
                ProductName = c.String(),
            })
        .PrimaryKey(t => t.Id);

    CreateTable(
        "KitComponents",
        c => new
            {
                Id = c.Int(nullable: false, identity: true),
                Component_Id = c.Int(),
                Product_Id = c.Int(),
            })
        .PrimaryKey(t => t.Id)
        .ForeignKey("Products", t => t.Component_Id)
        .ForeignKey("Products", t => t.Product_Id)
        .Index(t => t.Component_Id)
        .Index(t => t.Product_Id);

生成的外键名称:FK_KitComponents_Products_Product_Id FK_KitComponents_Products_Component_Id

如果我然后升级到 EF5 并更改外键迁移代码看起来像(注意"dbo.KitComponents" 和 "dbo.Products"而不是"KitComponents" 和 "Products"):

DropForeignKey("dbo.KitComponents", "Product_Id", "dbo.Products");
DropIndex("dbo.KitComponents", new[] { "Product_Id" });

并且更新数据库失败并显示消息:' FK_dbo.KitComponents_dbo.Products_Product_Id '不是约束。无法删除约束。请参阅以前的错误。

所以似乎从 EF5 开始,约束命名已从 FK_KitComponents_Products_Product_Id 更改为 FK_dbo.KitComponents_dbo.Products_Product_Id (带有 dbo. 前缀)

如何让 EF5 像在 EF 4.3 中一样运行,这样我就不必更改它吐出的每一段新迁移代码?

我还没有找到任何关于为什么会发生变化以及如何处理它的发行说明:(

4

4 回答 4

32

您可以通过对类进行子CSharpMigrationCodeGenerator类化来自定义生成的代码:

class MyCodeGenerator : CSharpMigrationCodeGenerator
{
    protected override void Generate(
        DropIndexOperation dropIndexOperation, IndentedTextWriter writer)
    {
        dropIndexOperation.Table = StripDbo(dropIndexOperation.Table);

        base.Generate(dropIndexOperation, writer);
    }

    // TODO: Override other Generate overloads that involve table names

    private string StripDbo(string table)
    {
        if (table.StartsWith("dbo."))
        {
            return table.Substring(4);
        }

        return table;
    }
}

然后在您的迁移配置类中设置它:

class Configuration : DbMigrationsConfiguration<MyContext>
{
    public Configuration()
    {
        CodeGenerator = new MyCodeGenerator();
    }
}
于 2012-08-21T18:34:28.620 回答
5

对于自动迁移,请使用以下代码:

public class MyOwnMySqlMigrationSqlGenerator : MySqlMigrationSqlGenerator
{
    protected override MigrationStatement Generate(AddForeignKeyOperation addForeignKeyOperation)
    {
        addForeignKeyOperation.PrincipalTable = addForeignKeyOperation.PrincipalTable.Replace("dbo.", "");
        addForeignKeyOperation.DependentTable = addForeignKeyOperation.DependentTable.Replace("dbo.", "");
        MigrationStatement ms = base.Generate(addForeignKeyOperation);
        return ms;
    }
}

并将其设置为配置:

SetSqlGenerator("MySql.Data.MySqlClient", new MyOwnMySqlMigrationSqlGenerator());
于 2014-05-14T13:53:06.323 回答
2

这是一个很好的答案,但是,如果您只是在寻找“快速修复”方法,那么当键位于基类中时,EF Migrations DropForeignKey 也会失败

使用包含参数 principalName 和 name 的 DropForeignKey 重载——在这种情况下,这意味着约束名称!

于 2012-09-14T22:16:02.483 回答
1

改进了 bricelam 的答案,我在 EF6 上尝试了这个。进行了一些更改以将架构保留为表名的一部分,并且仅将其从 FK 或 PK 名称中删除

internal class MyCodeGenerator : CSharpMigrationCodeGenerator
{
    protected override void Generate(AddForeignKeyOperation addForeignKeyOperation, IndentedTextWriter writer)
    {
        addForeignKeyOperation.Name = this.StripDbo(addForeignKeyOperation.Name, addForeignKeyOperation.DependentTable);
        addForeignKeyOperation.Name = this.StripDbo(addForeignKeyOperation.Name, addForeignKeyOperation.PrincipalTable);
        base.Generate(addForeignKeyOperation, writer);
    }

    protected override void Generate(AddPrimaryKeyOperation addPrimaryKeyOperation, IndentedTextWriter writer)
    {
        addPrimaryKeyOperation.Name = StripDbo(addPrimaryKeyOperation.Name, addPrimaryKeyOperation.Table);
        base.Generate(addPrimaryKeyOperation, writer);
    }

    protected override void Generate(DropForeignKeyOperation dropForeignKeyOperation, IndentedTextWriter writer)
    {
        dropForeignKeyOperation.Name = this.StripDbo(dropForeignKeyOperation.Name, dropForeignKeyOperation.DependentTable);
        dropForeignKeyOperation.Name = this.StripDbo(dropForeignKeyOperation.Name, dropForeignKeyOperation.PrincipalTable);
        base.Generate(dropForeignKeyOperation, writer);
    }

    protected override void Generate(DropPrimaryKeyOperation dropPrimaryKeyOperation, IndentedTextWriter writer)
    {
        dropPrimaryKeyOperation.Name = StripDbo(dropPrimaryKeyOperation.Name, dropPrimaryKeyOperation.Table);
        base.Generate(dropPrimaryKeyOperation, writer);
    }

    private string StripDbo(string objectName, string tableName)
    {
        if (tableName.StartsWith("dbo."))
        {
            return objectName.Replace(tableName, tableName.Substring(4));
        }

        return objectName;
    }
}
于 2014-12-24T19:50:49.157 回答