6

最初に Entity Framework 4.3.1 コードを使用し、データ移行を行います。

MigratorScriptingDecorator を使用して、ターゲット データベースの移行スクリプトを自動的に生成するユーティリティを作成しました。

ただし、ターゲット データベースを最初から再生成する場合、生成されたスクリプトが無効である場合があり、同じ名前の変数が 2 回宣言されます。

変数名は@var0です。

これは、複数の移行が適用されている場合、および少なくとも 2 つの移行でデフォルトの制約が削除された場合に発生するようです。

この問題は、スクリプト フォーム コードを生成するときと、パッケージ マネージャー コンソール コマンドを使用するときに発生します。

Update-Database -Script

生成されたスクリプトから問題のあるスニペットを次に示します。

DECLARE @var0 nvarchar(128)
SELECT @var0 = name
FROM sys.default_constraints
WHERE parent_object_id = object_id(N'SomeTableName')

DECLARE @var0 nvarchar(128)
SELECT @var0 = name
FROM sys.default_constraints
WHERE parent_object_id = object_id(N'SomeOtherTableName')

移行ごとにSQLを生成するポイントをオーバーライドしてから、「GO」ステートメントを追加して、各移行が個別のバッチになるようにしたいと考えています。これにより、問題が解決します。

誰でもこれを行う方法を知っていますか、または私が間違ったツリーを吠えている場合は、より良いアプローチを提案できますか?

4

1 に答える 1

4

そのため、この質問への回答でILSpyといくつかのポインターを広範に使用することで、方法を見つけました。

興味のある方は以下詳細を。

問題

SqlServerMigrationSqlGenerator、ターゲット データベースに対して実行される SQL ステートメントの作成、または-Scriptパッケージ マネージャー コンソールでスイッチを使用する場合、またはMigratorScriptingDecorator.

仕組み

SqlServerMigrationSqlGeneratorを担当する の Genearate メソッドを調べると、DROP COLUMN次のようになります。

protected virtual void Generate(DropColumnOperation dropColumnOperation)
{
    RuntimeFailureMethods
        .Requires(dropColumnOperation != null, null, "dropColumnOperation != null");
    using (IndentedTextWriter indentedTextWriter = 
        SqlServerMigrationSqlGenerator.Writer())
    {
        string value = "@var" + this._variableCounter++;
        indentedTextWriter.Write("DECLARE ");
        indentedTextWriter.Write(value);
        indentedTextWriter.WriteLine(" nvarchar(128)");
        indentedTextWriter.Write("SELECT ");
        indentedTextWriter.Write(value);
        indentedTextWriter.WriteLine(" = name");
        indentedTextWriter.WriteLine("FROM sys.default_constraints");
        indentedTextWriter.Write("WHERE parent_object_id = object_id(N'");
        indentedTextWriter.Write(dropColumnOperation.Table);
        indentedTextWriter.WriteLine("')");
        indentedTextWriter.Write("AND col_name(parent_object_id, 
                                                       parent_column_id) = '");
        indentedTextWriter.Write(dropColumnOperation.Name);
        indentedTextWriter.WriteLine("';");
        indentedTextWriter.Write("IF ");
        indentedTextWriter.Write(value);
        indentedTextWriter.WriteLine(" IS NOT NULL");
        indentedTextWriter.Indent++;
        indentedTextWriter.Write("EXECUTE('ALTER TABLE ");
        indentedTextWriter.Write(this.Name(dropColumnOperation.Table));
        indentedTextWriter.Write(" DROP CONSTRAINT ' + ");
        indentedTextWriter.Write(value);
        indentedTextWriter.WriteLine(")");
        indentedTextWriter.Indent--;
        indentedTextWriter.Write("ALTER TABLE ");
        indentedTextWriter.Write(this.Name(dropColumnOperation.Table));
        indentedTextWriter.Write(" DROP COLUMN ");
        indentedTextWriter.Write(this.Quote(dropColumnOperation.Name));
        this.Statement(indentedTextWriter);
    }
}

使用されている変数名を追跡していることがわかりますが、これはバッチ内、つまり単一の migration 内で追跡しているように見えます。そのため、移行に複数のものが含まれている場合DROP COLUM、上記は正常に機能しますが、2 つの移行DROP COLUMNが生成される結果になる場合、_variableCounter変数はリセットされます。

各ステートメントはデータベースに対してすぐに実行されるため、スクリプトを生成しない場合は問題は発生しません (SQL プロファイラーを使用して確認しました)。

SQL スクリプトを生成し、それをそのまま実行したい場合、問題があります。

解決

次のように新しいBatchSqlServerMigrationSqlGenerator継承を作成しましたSqlServerMigrationSqlGenerator(必要に注意してくださいusing System.Data.Entity.Migrations.Sql;):

public class BatchSqlServerMigrationSqlGenerator : SqlServerMigrationSqlGenerator
{
    protected override void Generate
       (System.Data.Entity.Migrations.Model.DropColumnOperation dropColumnOperation)
    {
        base.Generate(dropColumnOperation);

        Statement("GO");
    }
}

移行でカスタム ジェネレーターを使用するように強制するには、次の 2 つのオプションがあります。

  1. パッケージ マネージャー コンソールに統合する場合は、以下の行をConfigurationクラスに追加します。

       SetSqlGenerator("System.Data.SqlClient", 
                       new BatchSqlServerMigrationSqlGenerator());
    
  2. コードからスクリプトを生成している場合 (私のように)、同様のコード行をコード内の構成アセンブリがある場所に追加します。

    migrationsConfiguration.SetSqlGenerator(DataProviderInvariantName, 
                       new BatchSqlServerMigrationSqlGenerator());
    
于 2012-06-07T12:23:17.037 に答える