我最近使用以下方法将项目从数据库优先更新为代码优先模型:Link
在我想更新现有表上的FK和PK之前,一切似乎都可以正常工作。
这是1-0、1-1的关系。因此,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
关系。即1家公司有许多DriverScorecardSetting
。
所以我添加了一个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");
}
}
当我在Package Manager控制台中运行此迁移时,出现错误,因为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约定Link进行编码来解决此问题,但是我如何重命名约定名称?它只是一个内部set属性。
我正在使用EF v6.2。
答案 0 :(得分:3)
这是Code First to an Existing Database工作流程的一个已知问题,在EF6文档的Code First Migrations with an existing database - Things to be aware of部分中进行了解释:
默认/计算出的名称可能与现有架构不匹配
迁移在支持迁移时会显式指定列和表的名称。但是,在应用迁移时,迁移还会为其他数据库对象计算默认名称。这包括索引和外键约束。定位现有模式时,这些计算出的名称可能与数据库中实际存在的名称不匹配。
,建议的解决方案是手动编辑生成的迁移代码并利用可选的name
参数(如另一个答案所述):
如果模型中将来的更改需要更改或删除名称不同的数据库对象之一,则需要修改支架式迁移以指定正确的名称。 Migrations API具有一个可选的Name参数,您可以通过该参数执行此操作。例如,您现有的架构可能具有一个Post表,该表具有一个BlogId外键列,该列具有名为IndexFk_BlogId的索引。但是,默认情况下,Migrations希望该索引名为IX_BlogId。如果您对模型进行更改以导致删除该索引,则需要修改支架的DropIndex调用以指定IndexFk_BlogId名称。
当然没有人愿意手动执行此操作。不幸的是,正如我在对Unique Indexes convention in EF6的答复中提到的那样,PK和FK约束名称的问题在于EF6没有用于控制它们的元数据项/属性/注释。如果有这种方法,则逆向工程流程很可能会使用它。但是为了百分百地确定,我已经检查了源代码,尽管ForeignKeyOperation
和PrimaryKeyOperation
都有可设置的属性Name
,但除脚手架的迁移电话。
很快,约定的想法就死了。还有什么可以做的?好吧,虽然我们无法使用元数据来控制它,但幸运的是,我们可以通过自定义MigrationCodeGenerator类来控制迁移代码的生成 :
提供程序的基类,这些提供程序为基于代码的迁移生成代码。
因为这是C#,所以我们将继承CSharpMigrationCodeGenerator,重写Generate方法,对每个ForeignKeyOperation
和PrimaryKeyOperation
应用命名约定,然后让基数执行其余操作。示例实现可能是这样的:
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(")");
}
}
请注意,我们还需要重写(替换)GenerateInline(AddForeignKeyOperation
方法的基本实现(该方法在创建FK作为create table操作的一部分时使用),因为当前它有一个错误会忽略Name
属性(请参见代码中的注释)。
完成此操作后,您只需通过在DbMigrationsConfiguration
派生类构造函数中设置CodeGenerator属性来替换标准的迁移代码生成器:
internal sealed class Configuration : DbMigrationsConfiguration<MyDbContext>
{
public Configuration()
{
CodeGenerator = new CustomMigrationCodeGenerator();
// ...
}
}
答案 1 :(得分:1)
您可以在创建的迁移中修改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
}
}
链接: