17

EntityFramework5.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");
}

次に、dbを更新しました:

update-database -verbose

そして、私が期待していたように、2つの列の名前を変更しました。

いくつかの移行の後、本番環境をバックアップし、ローカルDBに復元して、このDBのコードをテストしました。このDBには、TraversalZonesテーブルがすでに作成されており、古い列名(LowおよびHigh)があります。もちろん、更新することから始めました。

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.​

したがって、明らかに、「左」列をこのテーブルに入れる必要があるかどうかについて、移行は混乱しています。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')

これは、移行のバグのようです。

4

1 に答える 1

17

回避策は、明らかにこれです:

update-database -f -script

あなたは私の質問で結果を見ることができます。次に、スクリプトから最後の行以外のすべてを投げ、それをDBに対して実行して、移行に通知しました。すでにその列の名前を変更し、切り取っています。

これでデータベースのこのコピーを続行できますが、Productionのコピーに対するすべての移行(Production自体が移行されるまで)でこの問題が発生し続けるのではないかと心配しています。この回避策なしでこれを適切に解決するにはどうすればよいですか?

アップデート

これは実際、本番環境を含む他のすべてのインスタンスで問題でした。汚い解決策は、生成されたupdate-database -f -scriptバージョンと修正されたバージョンをコミットした後、SQLスクリプト()を生成することでした。

少しクリーンな解決策は、スクリプトからSQLを取得し、手動移行を追加して、Upの内容を次のように変更することです。

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

これにより、この移行を実行している他の環境が意図したとおりに正確に実行されるようになります。

これをテストするのは少し難しいので、次のようにアプローチできます。

  1. 万が一に備えて、データベースをバックアップしてください。
  2. SQLを実行します。正しく機能する場合は、SQLを脇に置いておきます。
  3. 手動移行を追加し、Up()メソッドですべてを消去します。完全に空のままにします。
  4. update-database-fを実行します
  5. Sql("...");次に、取っておいたSQLの呼び出しを追加して、Up()メソッドを変更します。

これで、SQLを2回実行しなくてもデータベースが最新になり、他の環境でそのSQLの結果が得られます。

于 2012-12-19T16:45:01.650 に答える