C# 数据库优先到代码优先EF-迁移键和约束名称与数据库不匹配
我最近使用以下方法将我的项目从“数据库优先”更新为“代码优先”模型: 在我想更新现有表上的FK和PKs之前,一切似乎都正常工作 这是一种1-0,1-1的关系。因此,C# 数据库优先到代码优先EF-迁移键和约束名称与数据库不匹配,c#,entity-framework,ef-code-first,entity-framework-migrations,ef-database-first,C#,Entity Framework,Ef Code First,Entity Framework Migrations,Ef Database First,我最近使用以下方法将我的项目从“数据库优先”更新为“代码优先”模型: 在我想更新现有表上的FK和PKs之前,一切似乎都正常工作 这是一种1-0,1-1的关系。因此,Company表的PK是DriverScorecardSetting表的FK和PK 因此,这就是工具为DriverScorecardSetting表生成的实体 [Table("DriverScorecardSetting")] public partial class DriverScorecardSetting { [Ke
Company
表的PK是DriverScorecardSetting
表的FK和PK
因此,这就是工具为DriverScorecardSetting
表生成的实体
[Table("DriverScorecardSetting")]
public partial class DriverScorecardSetting
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int iCompanyId { get; set; }
public virtual Company Company { get; set; }
....
}
现在我想更新关系并使其成为1-N
关系。i、 e.1公司许多驾驶员CoreCardSetting
因此,我添加了一个PK,并将关系转换为1-N
[Table("DriverScorecardSetting")]
public partial class DriverScorecardSetting
{
[Key]
public int iDriverScorecardSettingId { get; set; }
[ForeignKey("Company")]
public int iCompanyId { get; set; }
public virtual Company Company { get; set; }
...
}
我还对公司实体进行了更改
问题是当我添加迁移时。密钥的名称与数据库中现有的密钥不同。因此,当我运行迁移时,它在数据库中找不到名称,并且不会删除它们
这就是它创建的迁移
public partial class PKForDriverScorecardSetting : DbMigration
{
public override void Up()
{
DropForeignKey("dbo.DriverScorecardSetting", "iCompanyId", "dbo.Companies");
DropPrimaryKey("dbo.DriverScorecardSetting");
AddColumn("dbo.DriverScorecardSetting", "iDriverScorecardSettingId", c => c.Int(nullable: false, identity: true));
AddPrimaryKey("dbo.DriverScorecardSetting", "iDriverScorecardSettingId");
AddForeignKey("dbo.DriverScorecardSetting", "iCompanyId", "dbo.Companies", "iCompanyId", cascadeDelete: true);
}
public override void Down()
{
DropForeignKey("dbo.DriverScorecardSetting", "iCompanyId", "dbo.Companies");
DropPrimaryKey("dbo.DriverScorecardSetting");
DropColumn("dbo.DriverScorecardSetting", "iDriverScorecardSettingId");
AddPrimaryKey("dbo.DriverScorecardSetting", "iCompanyId");
AddForeignKey("dbo.DriverScorecardSetting", "iCompanyId", "dbo.Companies", "iCompanyId");
}
}
当我在PackageManager控制台中运行此迁移时,会出现错误,因为EF生成的约束的名称是错误的。这是生成的脚本
IF object_id(N'[dbo].[FK_dbo.DriverScorecardSetting_dbo.Companies_iCompanyId]', N'F') IS NOT NULL
ALTER TABLE [dbo].[DriverScorecardSetting] DROP CONSTRAINT [FK_dbo.DriverScorecardSetting_dbo.Companies_iCompanyId]
ALTER TABLE [dbo].[DriverScorecardSetting] DROP CONSTRAINT [PK_dbo.DriverScorecardSetting]
ALTER TABLE [dbo].[DriverScorecardSetting] ADD [iDriverScorecardSettingId] [int] NOT NULL IDENTITY
ALTER TABLE [dbo].[DriverScorecardSetting] ADD CONSTRAINT [PK_dbo.DriverScorecardSetting] PRIMARY KEY ([iDriverScorecardSettingId])
ALTER TABLE [dbo].[DriverScorecardSetting] ADD CONSTRAINT [FK_dbo.DriverScorecardSetting_dbo.Companies_iCompanyId] FOREIGN KEY ([iCompanyId]) REFERENCES [dbo].[Companies] ([iCompanyId]) ON DELETE CASCADE
但是约束的初始名称不包括
和dbo
现在我知道也许有一种方法可以通过编码FK约定来解决这个问题,但是我该如何重命名约定名称呢?它只是一个内部集合属性
我正在使用EF v6.2。您可以在创建的迁移中修改Up()和Down()方法。使用使用外键名称的DropForeignKey重载。DropPrimaryKey也需要更改
public partial class PKForDriverScorecardSetting : DbMigration
{
public override void Up()
{
//DropForeignKey("dbo.DriverScorecardSetting", "iCompanyId", "dbo.Companies"); // different name
DropForeignKey("dbo.DriverScorecardSetting", "FK_DriverScorecardSetting_Companies"); // drop FK by name
//DropPrimaryKey("dbo.DriverScorecardSetting"); // different name
DropPrimaryKey("dbo.DriverScorecardSetting", "PK_DriverScorecardSetting"); // drop PK by name
AddColumn("dbo.DriverScorecardSetting", "iDriverScorecardSettingId", c => c.Int(nullable: false, identity: true));
AddPrimaryKey("dbo.DriverScorecardSetting", "iDriverScorecardSettingId");
AddForeignKey("dbo.DriverScorecardSetting", "iCompanyId", "dbo.Companies", "iCompanyId", cascadeDelete: true);
}
public override void Down()
{
DropForeignKey("dbo.DriverScorecardSetting", "iCompanyId", "dbo.Companies");
DropPrimaryKey("dbo.DriverScorecardSetting");
DropColumn("dbo.DriverScorecardSetting", "iDriverScorecardSettingId");
//AddPrimaryKey("dbo.DriverScorecardSetting", "iCompanyId");// different name
AddPrimaryKey("dbo.DriverScorecardSetting", "iCompanyId", name:"PK_DriverScorecardSetting");// Add PK with name
//AddForeignKey("dbo.DriverScorecardSetting", "iCompanyId", "dbo.Companies", "iCompanyId");// different name
AddForeignKey("dbo.DriverScorecardSetting", "iCompanyId", "dbo.Companies", "iCompanyId", name:"FK_DriverScorecardSetting_Companies");// different name
}
}
链接:
name
参数(如另一个答案所述):
如果模型中的未来更改需要更改或删除一个命名不同的数据库对象,则需要修改脚手架迁移以指定正确的名称。迁移API有一个可选的名称参数,允许您执行此操作。例如,您现有的模式可能有一个带有BlogId外键列的Post表,该列的索引名为IndexFk_BlogId。但是,默认情况下,迁移会将此索引命名为IX_BlogId。如果对模型进行更改导致删除此索引,则需要修改scaffold DropIndex调用以指定IndexFk_BlogId名称
当然,没有人愿意手动操作。不幸的是,正如我在对的回答中提到的,PK和FK约束名称的问题是EF6没有元数据项/属性/注释来控制它们。如果有这样一种方法,很可能逆向工程过程会使用它。但是为了百分之百地确定,我检查了源代码,尽管ForeignKeyOperation
和PrimaryKeyOperation
都有一个可设置的属性Name
,但除了框架式迁移调用之外,其他任何操作都没有指定它
很快,传统观念就消失了。还可以做些什么?虽然我们无法通过元数据控制这一点,但幸运的是,我们可以通过自定义类控制迁移代码的生成:
为基于代码的迁移生成代码的提供程序的基类
由于这是C#,我们将继承、重写该方法,对每个ForeignKeyOperation
和PrimaryKeyOperation
应用我们的命名约定,并让base完成其余的工作。示例实现可以如下所示:
using System;
using System.Collections.Generic;
using System.Data.Entity.Migrations.Design;
using System.Data.Entity.Migrations.Model;
using System.Data.Entity.Migrations.Utilities;
using System.Linq;
class CustomMigrationCodeGenerator : CSharpMigrationCodeGenerator
{
public override ScaffoldedMigration Generate(string migrationId, IEnumerable<MigrationOperation> operations, string sourceModel, string targetModel, string @namespace, string className)
{
foreach (var fkOperation in operations.OfType<ForeignKeyOperation>()
.Where(op => op.HasDefaultName))
{
fkOperation.Name = fkOperation.Name.Replace("dbo.", "");
// or generate FK name using DependentTable, PrincipalTable and DependentColumns properties,
// removing schema from table names if needed
}
foreach (var pkOperation in operations.OfType<PrimaryKeyOperation>()
.Concat(operations.OfType<CreateTableOperation>().Select(op => op.PrimaryKey))
.Where(op => op.HasDefaultName))
{
pkOperation.Name = pkOperation.Name.Replace("dbo.", "");
// or generate PK name using Table and Columns properties,
// removing schema from table name if needed
}
return base.Generate(migrationId, operations, sourceModel, targetModel, @namespace, className);
}
protected override void GenerateInline(AddForeignKeyOperation addForeignKeyOperation, IndentedTextWriter writer)
{
writer.WriteLine();
writer.Write(".ForeignKey(" + Quote(addForeignKeyOperation.PrincipalTable) + ", ");
Generate(addForeignKeyOperation.DependentColumns, writer);
if (addForeignKeyOperation.CascadeDelete)
writer.Write(", cascadeDelete: true");
// { missing in base implementation
if (!addForeignKeyOperation.HasDefaultName)
{
writer.Write(", name: ");
writer.Write(Quote(addForeignKeyOperation.Name));
}
// }
writer.Write(")");
}
}
我知道这可以在迁移文件本身中实现,但我正在寻找一种更通用的FK命名设置方法。否则,每次我需要更新数据库中的FK/PK时,我都必须修改迁移文件以匹配数据库。感谢您提供了示例的详细答案。我只是想知道有没有一种方法可以调试这段代码-
CustomMigrationCodeGenerator
?好问题-我也想知道!我通过PMC生成迁移来测试它,查看生成的文件/脚本,删除它,更改代码,然后再次执行,等等。结果表明,调试很简单,只需在需要进入的位置粘贴一个System.Diagnostics.Debugger.Launch()
。幸亏
internal sealed class Configuration : DbMigrationsConfiguration<MyDbContext>
{
public Configuration()
{
CodeGenerator = new CustomMigrationCodeGenerator();
// ...
}
}