5

OOB データベース初期化子を使用して作成した DB があり、EF 4.3.1 で Code First を使用しています。

Add-Migration コマンドレットの新しい "IgnoreChanges" フラグを利用して、列の一部を変更し、既定の SQL 値を追加したいと考えていました。基本的に、一部のエンティティには DateLastUpdated という名前の列があり、DEFAULT を SQL 式 GETDATE() に設定したいと考えています。

「add-migration InitialMigration -ignorechanges」を使用して InitialMigration を作成し、Up() と Down() に以下を追加しました。

public override void Up()
{
    AlterColumn("CustomerLocations", "DateLastUpdated", c => c.DateTime(defaultValueSql: "GETDATE()"));
    AlterColumn("UserReportTemplates", "DateLastUpdated", c => c.DateTime(defaultValueSql: "GETDATE()"));
    AlterColumn("Chains", "DateLastUpdated", c => c.DateTime(defaultValueSql: "GETDATE()"));
}

public override void Down()
{
    AlterColumn("CustomerLocations", "DateLastUpdated", c => c.DateTime());
    AlterColumn("UserReportTemplates", "DateLastUpdated", c => c.DateTime());
    AlterColumn("Chains", "DateLastUpdated", c => c.DateTime());
}

次に、「Update-Database -verbose」を実行してみましたが、データベースに同じ名前の既定の制約を作成しようとしていることがわかり、SQL は例外をスローします。

Applying explicit migrations: [201203221856095_InitialMigration].
Applying explicit migration: 201203221856095_InitialMigration.
ALTER TABLE [CustomerLocations] ADD CONSTRAINT DF_DateLastUpdated DEFAULT GETDATE() FOR [DateLastUpdated]
ALTER TABLE [CustomerLocations] ALTER COLUMN [DateLastUpdated] [datetime]
ALTER TABLE [UserReportTemplates] ADD CONSTRAINT DF_DateLastUpdated DEFAULT GETDATE() FOR [DateLastUpdated]
System.Data.SqlClient.SqlException (0x80131904): There is already an object named 'DF_DateLastUpdated' in the database.
Could not create constraint. See previous errors.
   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.RunExecuteNonQueryTds(String methodName, Boolean async)
   at System.Data.SqlClient.SqlCommand.InternalExecuteNonQuery(DbAsyncResult result, String methodName, Boolean sendToPipe)
   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)
   at System.Data.Entity.Migrations.DbMigrator.ApplyMigration(DbMigration migration, DbMigration lastMigration)
   at System.Data.Entity.Migrations.Infrastructure.MigratorLoggingDecorator.ApplyMigration(DbMigration migration, DbMigration lastMigration)
   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()
There is already an object named 'DF_DateLastUpdated' in the database.
Could not create constraint. See previous errors.

EF は、列の名前に「DF_」を追加することで DEFAULT 制約を作成しているように見えますが、テーブルの名前は使用せず、これをテーブルに固有のものにしています。これは既知のバグですか、それともここで何か間違ったことをしていますか?

4

3 に答える 3

7

既知のバグのようです:msdnフォーラム

Andrew J Peters Microsoft(MSFT)は次のように答えました。

これを報告していただきありがとうございます。この問題はRTMで修正されます。

考えられる回避策は、最初に列をnull許容にすることです。これにより、移行で追加のDEFAULT制約が生成されなくなります。列が作成されると、null不可に戻すことができます。

しかし、それはEF4.3.1では完全に修正されていません。ソースの関連部分は次のとおりです。

// Type: System.Data.Entity.Migrations.Sql.SqlServerMigrationSqlGenerator
// Assembly: EntityFramework, Version=4.3.1.0, 
// Culture=neutral, PublicKeyToken=b77a5c561934e089
namespace System.Data.Entity.Migrations.Sql
{
  public class SqlServerMigrationSqlGenerator : MigrationSqlGenerator
  {
     protected virtual void Generate(AlterColumnOperation alterColumnOperation)
     {
      //...
      writer.Write("ALTER TABLE ");
      writer.Write(this.Name(alterColumnOperation.Table));
      writer.Write(" ADD CONSTRAINT DF_");
      writer.Write(column.Name);
      writer.Write(" DEFAULT ");
      //...

したがって、EFは制約名を一意にしようとはしません。

回避策を試して、バグとして報告する必要があります。

編集:上記のGenerate方法はvirtual、最悪の場合SqlServerMigrationSqlGenerator、SQL生成を継承して修正し、Configuration.csでSQLジェネレーターとして設定できることに気づきました。

public Configuration()
{
    AutomaticMigrationsEnabled = true;
    SetSqlGenerator("System.Data.SqlClient", 
        new MyFixedSqlServerMigrationSqlGenerator());
}

編集2:

生のSQLにフォールバックするように修正されるまで、最善の方法だと思います。

public override void Up()
{
    Sql(@"ALTER TABLE [CustomerLocations] ADD CONSTRAINT 
        DF_CustomerLocations_DateLastUpdated 
        DEFAULT GETDATE() FOR [DateLastUpdated]");
    Sql(@"ALTER TABLE [CustomerLocations] ALTER COLUMN 
        [DateLastUpdated] [datetime]");
    //...
}
于 2012-03-22T22:17:23.630 に答える
7

わかりました。nemesvの回答(承認済み)に基づいて、修正が正式に発行されるまで、今のところ問題を修正することになった方法は次のとおりです。

internal class MyFixedSqlServerMigrationSqlGenerator : SqlServerMigrationSqlGenerator
{
    protected override void Generate(AlterColumnOperation alterColumnOperation)
    {
        if (alterColumnOperation == null)
            throw new ApplicationException("alterColumnOperation != null");

        ColumnModel column = alterColumnOperation.Column;
        if ((column.DefaultValue != null) || !string.IsNullOrWhiteSpace(column.DefaultValueSql))
        {
            using (IndentedTextWriter writer = Writer())
            {
                writer.Write("ALTER TABLE ");
                writer.Write(this.Name(alterColumnOperation.Table));
                writer.Write(" ADD CONSTRAINT DF_");
                writer.Write(alterColumnOperation.Table + "_"); //   <== THIS IS THE LINE THAT FIXES THE PROBLEM
                writer.Write(column.Name);
                writer.Write(" DEFAULT ");
                writer.Write(column.DefaultValue != null ? base.Generate(column.DefaultValue) : column.DefaultValueSql);
                writer.Write(" FOR ");
                writer.Write(this.Quote(column.Name));
                this.Statement(writer);
            }
        }
        using (IndentedTextWriter writer2 = Writer())
        {
            writer2.Write("ALTER TABLE ");
            writer2.Write(this.Name(alterColumnOperation.Table));
            writer2.Write(" ALTER COLUMN ");
            writer2.Write(this.Quote(column.Name));
            writer2.Write(" ");
            writer2.Write(this.BuildColumnType(column));
            if (column.IsNullable.HasValue && !column.IsNullable.Value)
            {
                writer2.Write(" NOT NULL");
            }
            this.Statement(writer2);
        }
    }
}


internal sealed class Configuration : DbMigrationsConfiguration<MyDbContext>
{
    public Configuration()
    {
        AutomaticMigrationsEnabled = true;

        SetSqlGenerator("System.Data.SqlClient", new MyFixedSqlServerMigrationSqlGenerator());
    }
    ...
}
于 2012-03-26T15:59:17.040 に答える