4

EF 4.3.1移行を使用していて、次のコードを持つConfigurationクラスがあります。

internal sealed class Configuration : DbMigrationsConfiguration<DbContext>
{
    public Configuration()
    {
        AutomaticMigrationsEnabled = false;
    }

    protected override void Seed(PayByPhoneDbContext context)
    {
        context.Roles.AddOrUpdate(r => r.Name, new Role { Name = "A" }, new Role { Name = "B" });
        context.Administrators.AddOrUpdate(a => a.Email, new Administrator { Email = "a@a.com" Name = "A", Role = context.Roles.Local.SingleOrDefault(role => role.Name == "A") });
    }
}

これで、DBが存在しないときにmigrateコマンド(MSBuildスクリプトの一部)を実行すると、テーブルが作成され、期待どおりにシードが実行されます。しかし、移行せずに既存のデータベースでmigrateコマンドを実行し、すべてのデータがすでにシードされている場合(挿入ではなく更新を行う必要がある場合)、migrateコマンドの実行時にエラーが発生します。

保留中の明示的な移行はありません。

Seedメソッドを実行しています。

System.Data.Entity.Infrastructure.DbUpdateException:エントリの更新中にエラーが発生しました。詳細については、内部例外を参照してください。---> System.Data.UpdateException:エントリの更新中にエラーが発生しました。詳細については、内部例外を参照してください。---> System.Data.SqlClient.SqlException:UPDATEステートメントがFOREIGNKEY制約「FK_Administrators_Roles_RoleId」と競合しました。データベース「xxxDB」、テーブル「dbo.Roles」、列「Id」で競合が発生しました。

ステートメントは終了されました。

スタックトレース:

at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection)
   at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection)
   at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning()
   at System.Data.SqlClient.TdsParser.Run(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj)
   at System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString)
   at System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async)
   at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, DbAsyncResult result)
   at System.Data.SqlClient.SqlCommand.InternalExecuteNonQuery(DbAsyncResult result, String methodName, Boolean sendToPipe)
   at System.Data.SqlClient.SqlCommand.ExecuteNonQuery()
   at System.Data.Mapping.Update.Internal.DynamicUpdateCommand.Execute(UpdateTranslator translator, EntityConnection connection, Dictionary`2 identifierValues, List`1 generatedValues)
   at System.Data.Mapping.Update.Internal.UpdateTranslator.Update(IEntityStateManager stateManager, IEntityAdapter adapter)
   --- End of inner exception stack trace ---
   at System.Data.Mapping.Update.Internal.UpdateTranslator.Update(IEntityStateManager stateManager, IEntityAdapter adapter)
   at System.Data.EntityClient.EntityAdapter.Update(IEntityStateManager entityCache)
   at System.Data.Objects.ObjectContext.SaveChanges(SaveOptions options)
   at System.Data.Entity.Internal.InternalContext.SaveChanges()
   --- End of inner exception stack trace ---
   at System.Data.Entity.Internal.InternalContext.SaveChanges()
   at System.Data.Entity.Internal.LazyInternalContext.SaveChanges()
   at System.Data.Entity.DbContext.SaveChanges()
   at System.Data.Entity.Migrations.DbMigrator.SeedDatabase()
   at System.Data.Entity.Migrations.Infrastructure.MigratorLoggingDecorator.SeedDatabase()
   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()

SQLServerプロファイラーの実行実行時にエラーが発生したことがわかりました。

exec sp_executesql N'update [dbo].[Administrators]
set [RoleId] = @0
where ([Id] = @1)
',N'@0 int,@1 bigint',@0=0,@1=1

Seedメソッドで外部キーを使用してデータをシードする正しい方法は何ですか?

4

1 に答える 1

5

AddOrUpdateは、指定していない列をnull(intの場合はゼロ)にリセットします。

私の理解では、この動作(ちなみにひどい)は、キーまたは自動インクリメントとして認識された列には適用されませんが、明らかにあなたの場合はそうです。

私のアドバイスは、役割がデータベースに存在しないことを確認するためのチェックを行い、存在しない場合は標準のadd()を実行することです。

シードしたデータを更新するためのインターフェイスを後でいつ提供できるかわからないため、すべてのシードデータに対してこれを行います。また、そのような状況でSeed()メソッドが変更を上書きすることは望ましくありません。

また、MigrateDatabaseToLatestVersionデータベース初期化子を使用することを選択した場合、アプリが再起動するたびにSeed()が起動することにも注意してください(dllデプロイメント、web.config更新など)。

Seed()メソッドで防御的にコーディングするのが最善です。

更新:Julie Lermanには、この動作に関する優れたブログ投稿があります。

于 2012-05-31T02:42:29.183 に答える