简体   繁体   English

如何在 EF Code First 中更改主键的名称?

[英]How to Change the name of a primary key in EF Code First?

I have a scenario where i would like to change the primary key name in an entity and be able to run update-database -force.我有一个场景,我想更改实体中的主键名称并能够运行 update-database -force。 See below for code and error getting when i try.当我尝试时,请参阅下面的代码和错误。

Entity was:实体是:

public class Team
{
    [Key]
    [HiddenInput(DisplayValue = false)]
    public virtual int Id { get; set; }

    [Display(Name = "Full Name:")]
    public virtual string Name { get; set; }
}

Entity Changed to:实体更改为:

public class Team
{
    [Key]
    [HiddenInput(DisplayValue = false)]
    public virtual int TeamId { get; set; }

    [Display(Name = "Full Name:")]
    public virtual string Name { get; set; }
}

When i run Update-database -Force i get the following error.当我运行Update-database -Force ,出现以下错误。

Multiple identity columns specified for table 'Teams'. Only one identity column per table is allowed.

Its a matter of naming convention and i need this to be TeamId when i reference it latter, simply Id conflicts with child entity classes.这是命名约定的问题,当我稍后引用它时,我需要它是 TeamId,只是 Id 与子实体类冲突。

Any ideas on how i can do this successfully?关于如何成功做到这一点的任何想法?

Depends on the version of EF you are using.取决于您使用的 EF 版本。 Even with migrations the result that you will see is something like:即使使用迁移,您将看到的结果类似于:

"drop column Id " and "add column TeamId". “删除列 ID”和“添加列 TeamId”。

With this you will lose all values and "child connections"......有了这个,您将失去所有价值和“子连接”......

The only "secure" solution I am seeing at this point is a mix of Migrations and "hand SQL operations".我目前看到的唯一“安全”解决方案是迁移和“手工 SQL 操作”的混合。

EASY solution:简单的解决方案:

1- taking in consideration that you already have a "base" migration creating the table with ID, now create the new migration with the "update". 1- 考虑到您已经有了创建带有 ID 的表的“基本”迁移,现在使用“更新”创建新的迁移。 Now do NOT run it yet.现在不要运行它。

2- Open that file and write a new line BEFORE the generated lines and use a SQL command, something like this: 2- 打开该文件并在生成的行之前写一个新行并使用 SQL 命令,如下所示:

     SQL("ALTER TABLE table_name RENAME COLUMN old_name to new_name;");

This will change the name BEFORE the migration deletes the column and create a new one, what will happen is: you change the name before the delete, then the delete is executed but it will "fail" but it will not hurt nothing.这将在迁移删除列并创建新列之前更改名称,将会发生的情况是:您在删除之前更改名称,然后执行删除但它会“失败”但不会有任何伤害。

But now you ask: why do I do this?但现在你问:我为什么要这样做? well if you are using migrations even if you delete the lines to delete the column and create a new one, the next time you create automatically a new migration file this new lines will be there...... this is why.好吧,如果您正在使用迁移,即使您删除行以删除列并创建一个新的列,下次您自动创建一个新的迁移文件时,这些新行也会在那里......这就是原因。

UPDATED ANSWERS #1更新的答案 #1

When I talk about Entity Framework Migrations I am referring to this: http://blogs.msdn.com/b/adonet/archive/2012/02/09/ef-4-3-code-based-migrations-walkthrough.aspx When you Run the 'Add-Migration AddBlogUrl' command in Package Manager Console, a new file (*.cs) is created.当我谈论实体框架迁移时,我指的是: http : //blogs.msdn.com/b/adonet/archive/2012/02/09/ef-4-3-code-based-migrations-walkthrough.aspx当您在包管理器控制台中运行“Add-Migration AddBlogUrl”命令时,会创建一个新文件 (*.cs)。

Example of this file migration file with SQL commands:此文件迁移文件与 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()
        {
            ........
        }

Easiest solution is to not rename the primary key in the database and instead map your class to your primary key and give it any name you want.最简单的解决方案是不重命名数据库中的主键,而是将您的类映射到您的主键并为其指定任何您想要的名称。 Like this:像这样:

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; }
}

Personally, I would keep the class name as Id.就个人而言,我会将班级名称保留为 Id。 The naming convention [TableName + Id] is old school and overkill for a primary key (for a foreign key it is good).命名约定 [TableName + Id] 是过时的,对于主键来说太过分了(对于外键来说很好)。 To me it just adds noise to your lines of code.对我来说,它只会给你的代码行增加噪音。 team.TeamId is no better than team.Id . team.TeamId并不比team.Id

After fiddling with suggestion by both marvc1 and emanyalpsid.在摆弄了 marvc1 和 emanyalpsid 的建议之后。 I decided to just drop the database and creating it again.我决定删除数据库并重新创建它。 This is done by simply deleting the database under Server Explorer in VS2012, and also make sure that the .mdf file under App_Data is also deleted.这通过在VS2012中简单地删除Server Explorer下的数据库来完成,并且还要确保App_Data下的.mdf文件也被删除。 The .mdf file is usually hidden, to see it just under Solution Explorer toolbar click on Show All Files and you will see it. .mdf 文件通常是隐藏的,要在解决方案资源管理器工具栏下查看它,单击显示所有文件,您将看到它。 when those steps are done, simply run the code below in Package Manager Console:完成这些步骤后,只需在包管理器控制台中运行以下代码:

update-database -Verbose

-Verbose simply lets you verify what you are creating. -Verbose 只是让您验证您正在创建的内容。

marvc1's Answer marvc1 的回答

Works just fine, except it does not change names in the database, if you are not too worried about database names, it is the safest way to go about it.工作得很好,除了它不会更改数据库中的名称,如果您不太担心数据库名称,这是最安全的方法。 By names in the database i mean, In the entity Team, Id would still be Id and not TeamId通过数据库中的名称,我的意思是, In the entity Team, Id would still be Id and not TeamId

User Dryadwoods recommends to execute the following sql inside migration:用户 Dryadwoods 建议在迁移内部执行以下 sql:

SQL("ALTER TABLE table_name RENAME COLUMN old_name to new_name;");

but :但 :

It is not possible to rename a column using the ALTER TABLE statement in MS SQL Server.无法在 MS SQL Server 中使用 ALTER TABLE 语句重命名列。 Use sp_rename instead.请改用 sp_rename。

so, I change it to the following code:所以,我将其更改为以下代码:

Sql("exec sp_rename 'dbo.Batches.BatchNo', 'Id', N'COLUMN';");

pay attention, that old name should be fully name, but new name should be short.注意,旧名要全名,新名要短。 Otherwise it will work incorrectly否则它将无法正常工作

I have a nice solution base on @Marvin Rounce solution.我有一个基于@Marvin Rounce 解决方案的不错的解决方案。 This solution apply the change both class and database very easily.此解决方案非常轻松地应用类和数据库的更改。

1. map your class, this step rename the column only in the database. 1.映射你的类,这一步只重命名数据库中的列。

public class Team

 {
        [Key]
        [HiddenInput(DisplayValue = false)]
        [Column("ID")] 
        public virtual int TeamId { get; set; }
    ....
    }

2. in Package Manager Console add a migration. 2.在包管理器控制台中添加迁移。

PM> Add-Migration rename_TeamID

3. update the database. 3.更新数据库。

PM> update-database

4. rename the column in the class to this name, this step rename the colum in your project too. 4.将类中的列重命名为该名称,这一步也将您项目中的列重命名。

note: you don't need the key attribute, the EF know that ID keyword is a primary key.注意:您不需要 key 属性,EF 知道 ID 关键字是主键。

public class Team
        {

       [HiddenInput(DisplayValue = false)]
       public virtual int ID { get; set; }

    ....
    }

you don't need to add migration because the last change doesn't affect database.您不需要添加迁移,因为最后一次更改不会影响数据库。

you can do this in 3 steps您可以通过 3 个步骤完成此操作

  1. Add a field with new name (in this case TeamId) and run the program.new field will be added添加具有新名称的字段(在本例中为 TeamId)并运行程序。将添加新字段

  2. Move [key] data annotation from old field to new field and run again.将 [key] 数据注释从旧字段移动到新字段并再次运行。 new field(TeamId) will be primary key.新字段(TeamId)将是主键。

  3. Delete old filed删除旧归档

this worked for me这对我有用

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM