entity-framework - EF5代码优先迁移:使用RenameColumn后,“每个表中的列名必须唯一”错误

标签 entity-framework ef-code-first entity-framework-5 entity-framework-migrations

我们正在使用Entity Framework 5.0代码优先和自动迁移。

我上过这样的课:

public class TraversalZones
{
    public int Low { get; set; }
    public int High { get; set; }
}​


然后我们意识到这些属性并不是真正正确的名称,因此我们更改了它们:

public class TraversalZones
{
    public int Left { get; set; }
    public int Top { get; set; }
}​


重命名在整个项目中都可以正确重构,但是我知道自动迁移还不够智能,无法在IDE中选择这些显式重命名,因此我首先检查以确认唯一的挂起迁移是此列重命名:

update-database -f -script


果然,它只是显示了SQL删除了Low和High并添加了Left和Top。然后,我添加了一个手动迁移:

add-migration RenameColumns_TraversalZones_LowHigh_LeftTop


并将生成的代码固定为:

public override void Up()
{
    RenameColumn("TraversalZones", "Low", "Left");
    RenameColumn("TraversalZones", "High", "Top");
}

public override void Down()
{
    RenameColumn("TraversalZones", "Left", "Low");
    RenameColumn("TraversalZones", "Top", "High");
}



然后,我更新了数据库:

update-database -verbose


就像我期望的那样,得到了2列重命名。

几次迁移之后,我备份了Production并将其还原到本地数据库以测试该数据库上的代码。这个数据库已经在其中创建了TraversalZones表,带有旧的列名(低和高),我当然是从更新它开始的:

update-database -f -verbose


重命名命令出现在输出中-一切都很好:

EXECUTE sp_rename @objname = N'TraversalZones.Low', @newname = N'Left', @objtype = N'COLUMN'
EXECUTE sp_rename @objname = N'TraversalZones.High', @newname = N'Top', @objtype = N'COLUMN'
[Inserting migration history record]


然后,我运行我的代码,它错误地告诉我自上次运行以来数据库已更改,因此我应该运行update-database ...。

所以我再次运行它:

update-database -f -verbose


现在卡在这个错误:

No pending code-based migrations. Applying automatic migration:
201212191601545_AutomaticMigration.
ALTER TABLE [dbo].[TraversalZones] ADD [Left] [int] NOT NULL DEFAULT 0
System.Data.SqlClient.SqlException (0x80131904): Column names in each table must be unique. Column name 'Left' in table 'dbo.TraversalZones' is specified more than once.
   at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
   at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
   at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)
   at System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady)
   at System.Data.SqlClient.SqlCommand.RunExecuteNonQueryTds(String methodName, Boolean async, Int32 timeout)
   at System.Data.SqlClient.SqlCommand.InternalExecuteNonQuery(TaskCompletionSource`1 completion, String methodName, Boolean sendToPipe, Int32 timeout, Boolean asyncWrite)
   at System.Data.SqlClient.SqlCommand.ExecuteNonQuery()
   at System.Data.Entity.Migrations.DbMigrator.ExecuteSql(DbTransaction transaction, MigrationStatement migrationStatement)
   at System.Data.Entity.Migrations.Infrastructure.MigratorLoggingDecorator.ExecuteSql(DbTransaction transaction, MigrationStatement migrationStatement)
   at System.Data.Entity.Migrations.DbMigrator.ExecuteStatements(IEnumerable`1 migrationStatements)
   at System.Data.Entity.Migrations.Infrastructure.MigratorBase.ExecuteStatements(IEnumerable`1 migrationStatements)
   at System.Data.Entity.Migrations.DbMigrator.ExecuteOperations(String migrationId, XDocument targetModel, IEnumerable`1 operations, Boolean downgrading, Boolean auto)
   at System.Data.Entity.Migrations.DbMigrator.AutoMigrate(String migrationId, XDocument sourceModel, XDocument targetModel, Boolean downgrading)
   at System.Data.Entity.Migrations.Infrastructure.MigratorLoggingDecorator.AutoMigrate(String migrationId, XDocument sourceModel, XDocument targetModel, Boolean downgrading)
   at System.Data.Entity.Migrations.DbMigrator.Upgrade(IEnumerable`1 pendingMigrations, String targetMigrationId, String lastMigrationId)
   at System.Data.Entity.Migrations.Infrastructure.MigratorLoggingDecorator.Upgrade(IEnumerable`1 pendingMigrations, String targetMigrationId, String lastMigrationId)
   at System.Data.Entity.Migrations.DbMigrator.Update(String targetMigration)
   at System.Data.Entity.Migrations.Infrastructure.MigratorBase.Update(String targetMigration)
   at System.Data.Entity.Migrations.Design.ToolingFacade.UpdateRunner.RunCore()
   at System.Data.Entity.Migrations.Design.ToolingFacade.BaseRunner.Run()
ClientConnectionId:c40408ee-def3-4553-a9fb-195366a05fff
Column names in each table must be unique. Column name 'Left' in table 'dbo.TraversalZones' is specified more than once.​


因此,对于“ Left”列是否仍需要将其放入该表中,显然,Migrations感到困惑。我假设RenameColumn会将事情保持在正确的状态,但似乎没有。

当我将尝试执行的操作转储到update-database -f -script时,我将尝试完全执行如果没有手动迁移的情况:

ALTER TABLE [dbo].[TraversalZones] ADD [Left] [int] NOT NULL DEFAULT 0
ALTER TABLE [dbo].[TraversalZones] ADD [Top] [int] NOT NULL DEFAULT 0
DECLARE @var0 nvarchar(128)
SELECT @var0 = name
FROM sys.default_constraints
WHERE parent_object_id = object_id(N'dbo.TraversalZones')
AND col_name(parent_object_id, parent_column_id) = 'Low';
IF @var0 IS NOT NULL
    EXECUTE('ALTER TABLE [dbo].[TraversalZones] DROP CONSTRAINT ' + @var0)
ALTER TABLE [dbo].[TraversalZones] DROP COLUMN [Low]
DECLARE @var1 nvarchar(128)
SELECT @var1 = name
FROM sys.default_constraints
WHERE parent_object_id = object_id(N'dbo.TraversalZones')
AND col_name(parent_object_id, parent_column_id) = 'High';
IF @var1 IS NOT NULL
    EXECUTE('ALTER TABLE [dbo].[TraversalZones] DROP CONSTRAINT ' + @var1)
ALTER TABLE [dbo].[TraversalZones] DROP COLUMN [High]
INSERT INTO [__MigrationHistory] ([MigrationId], [Model], [ProductVersion]) VALUES ('201212191639471_AutomaticMigration', 0x1F8B08000...000, '5.0.0.net40')


这似乎是迁移中的错误。

最佳答案

解决方法显然是这样的:

update-database -f -script


您可以在我的问题中看到哪些结果。然后,我将脚本中除最后一行以外的所有内容都扔掉了,然后将其针对数据库运行,以使Migrations知道:我们已经重命名了该列,并将其删除。

现在,我可以继续使用数据库的此副本,但是我担心针对生产副本的每次迁移(直到生产本身已迁移)都会继续出现此问题。没有此解决方法,如何正确解决此问题?

更新资料

实际上,这在包括生产在内的所有其他情况下都是一个问题。肮脏的解决方案是在提交生成的版本和固定版本后生成SQL脚本(update-database -f -script)。

稍微干净一点的解决方案是从脚本中获取SQL,添加手动迁移,然后将Up的内容更改为:

public void Up()
{
    Sql("...That SQL you extracted from the script...");
}


这将确保其他运行此迁移的环境能够按照您的预期精确执行。

测试这有些棘手,因此您可以通过以下方式进行处理:


备份您的数据库,以防万一。
运行SQL。如果工作正常,请搁置SQL。
添加手动迁移并清除Up()方法中的所有内容。使其完全为空。
运行update-database -f
现在,通过添加调用放置的SQL的Sql("...");来修改Up()方法。


现在,您的数据库是最新的,无需运行SQL两次,其他环境也可以获取该SQL的结果。

关于entity-framework - EF5代码优先迁移:使用RenameColumn后,“每个表中的列名必须唯一”错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13956905/

相关文章:

c# - EF 代码优先、播种和部署

c# - 创建 ThreadLocal EF 上下文

asp.net-mvc-3 - asp.NET MVC 3 复合模式模型绑定(bind)

entity-framework - EF Fluent API 多对一关系,未获取导航属性

c# - Orderby Ascending on a list 错误asp.net

c# - 从 Entity Framework 中的表值函数创建集合

nhibernate - NHibernate 上关于 Entity Framework 的导航属性是否有类似的东西?

c# - 带有 Oracle 表/ View 的 Entity Framework 5 不存在不一致?

entity-framework - Entity Framework 5的源码在哪里

c# - 如何限制绑定(bind)到 winforms 控件的大型数据源上 dbcontext 的大小