我有一个场景,我想更改实体中的主键名称,并能够运行update-database -force。当我尝试时,请参阅下面的代码和错误。
实体是:
public class Team
{
[Key]
[HiddenInput(DisplayValue = false)]
public virtual int Id { get; set; }
[Display(Name = "Full Name:")]
public virtual string Name { get; set; }
}
实体更改为:
public class Team
{
[Key]
[HiddenInput(DisplayValue = false)]
public virtual int TeamId { get; set; }
[Display(Name = "Full Name:")]
public virtual string Name { get; set; }
}
当我运行Update-database -Force
时,我收到以下错误。
Multiple identity columns specified for table 'Teams'. Only one identity column per table is allowed.
它是命名约定的问题,当我引用后者时,我需要将它作为TeamId,简单地说就是Id与子实体类冲突。
关于我如何成功完成任何想法?
取决于您使用的EF版本。即使迁移,您将看到的结果如下:
“drop column Id”和“add column TeamId”。
有了这个,你将失去所有的价值观和“儿童联系”......
我在这一点上看到的唯一“安全”解决方案是迁移和“手动SQL操作”的混合。
EASY解决方案:
1-考虑到您已经创建了具有ID的表的“基础”迁移,现在使用“更新”创建新的迁移。现在不要运行它。
2-打开该文件并在生成的行之前写一个新行并使用SQL命令,如下所示:
SQL("ALTER TABLE table_name RENAME COLUMN old_name to new_name;");
这将更改名称在迁移删除列并创建新列之前,将会发生的事情是:在删除之前更改名称,然后执行删除但它将“失败”但不会造成任何损害。
但现在你问:为什么我这样做?好吧,如果你使用迁移,即使你删除行删除列并创建一个新的,下次你自动创建一个新的迁移文件时,这些新的行将在那里......这就是原因。
更新的答案#1
当我谈到实体框架迁移时,我指的是:http://blogs.msdn.com/b/adonet/archive/2012/02/09/ef-4-3-code-based-migrations-walkthrough.aspx当您在软件包管理器控制台中运行“添加 - 迁移AddBlogUrl”命令时,会创建一个新文件(* .cs)。
带有SQL命令的此文件迁移文件的示例:
public partial class AddAbsencesTypesAndCategories : DbMigration
{
public override void Up()
{
CreateTable(
"pvw_AbsenceType",
c => new
{
Id = c.Int(nullable: false, identity: true),
Name = c.String(nullable: false),
CountAsVacation = c.Boolean(nullable: false),
IsIncremental = c.Boolean(nullable: false),
})
.PrimaryKey(t => t.Id);
.....
AddColumn("pvw_Absence", "CategoryId", c => c.Int(nullable: false));
AddForeignKey("pvw_Absence", "StatusId", "pvw_AbsenceStatusType", "Id");
AddForeignKey("pvw_Absence", "CategoryId", "pvw_AbsenceType", "Id");
CreateIndex("pvw_Absence", "StatusId");
CreateIndex("pvw_Absence", "CategoryId");
DropColumn("pvw_Absence", "MainCategoryId");
DropColumn("pvw_Absence", "SubCategoryId");
......
Sql(@"
SET IDENTITY_INSERT [dbo].[pvw_AbsenceStatusType] ON
INSERT pvw_AbsenceStatusType (Id, Name) VALUES (1, N'Entwurf')
SET IDENTITY_INSERT [dbo].[pvw_AbsenceStatusType] OFF
");
.....
}
public override void Down()
{
........
}
最简单的解决方案是不重命名数据库中的主键,而是将您的类映射到主键并为其指定任何名称。像这样:
public class Team
{
[Key]
[HiddenInput(DisplayValue = false)]
[Column("Id")] //this attribute maps TeamId to the column Id in the database
public virtual int TeamId { get; set; }
[Display(Name = "Full Name:")]
public virtual string Name { get; set; }
}
就个人而言,我会将班级名称保留为Id。命名约定[TableName + Id]是旧学校并且对于主键是过分的(对于外键来说它是好的)。对我来说,这只会给你的代码行增加噪音。 team.TeamId
并不比team.Id
好。
在摆弄了marvc1和emanyalpsid的建议之后。我决定放弃数据库并再次创建它。这是通过简单地删除VS2012中的服务器资源管理器下的数据库来完成的,并确保也删除了App_Data下的.mdf文件。 .mdf文件通常是隐藏的,只需在解决方案资源管理器工具栏下单击“显示所有文件”,您就会看到它。完成这些步骤后,只需在Package Manager Console中运行以下代码:
update-database -Verbose
-Verbose只是让你验证你正在创建什么。
marvc1的答案
工作得很好,除了它不会改变数据库中的名称,如果你不太担心数据库名称,这是最安全的方法。通过数据库中的名字,我的意思是,In the entity Team, Id would still be Id and not TeamId
用户Dryadwoods建议在迁移中执行以下sql:
SQL("ALTER TABLE table_name RENAME COLUMN old_name to new_name;");
但是:
使用MS SQL Server中的ALTER TABLE语句无法重命名列。请改用sp_rename。
所以,我将其更改为以下代码:
Sql("exec sp_rename 'dbo.Batches.BatchNo', 'Id', N'COLUMN';");
注意,那个旧名称应该是全名,但新名称应该简短。否则它将无法正常工作
我有一个很好的解决方案基于@Marvin Rounce解决方案。此解决方案非常容易应用类和数据库的更改。
1.映射您的类,此步骤仅在数据库中重命名该列。
public class Team
{
[Key]
[HiddenInput(DisplayValue = false)]
[Column("ID")]
public virtual int TeamId { get; set; }
....
}
2.在程序包管理器控制台中添加迁移。
PM> Add-Migration rename_TeamID
3.更新数据库。
PM> update-database
4.将类中的列重命名为此名称,此步骤也重命名项目中的列。
注意:您不需要key属性,EF知道ID关键字是主键。
public class Team
{
[HiddenInput(DisplayValue = false)]
public virtual int ID { get; set; }
....
}
您不需要添加迁移,因为上次更改不会影响数据库。