6

首先使用 Entity Framework 4.3.1 代码和数据迁移。

我编写了一个实用程序,使用 MigratorScriptingDecorator 自动为目标数据库生成迁移脚本。

但是,有时从头重新生成目标数据库时,生成的脚本是无效的,因为它两次声明了同名的变量。

变量名称是@var0

当应用多个迁移并且至少有两个导致删除默认约束时,这似乎会发生。

生成脚本表单代码和使用包管理器控制台命令时都会出现此问题:

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检查负责 a的 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);
    }
}

您可以看到它跟踪使用的变量名称,但这似乎只在批次内跟踪,即单个迁移。因此,如果一个迁移包含多个DROP COLUM以上的工作正常,但如果有两个迁移导致DROP COLUMN生成一个,那么该_variableCounter变量将被重置。

不生成脚本时不会出现任何问题,因为每个语句都会立即针对数据库执行(我使用 SQL Profiler 检查过)。

如果您生成一个 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");
    }
}

现在要强制迁移使用您的自定义生成器,您有两个选择:

  1. 如果您希望将其集成到包管理器控制台中,请将以下行添加到您的Configuration类中:

       SetSqlGenerator("System.Data.SqlClient", 
                       new BatchSqlServerMigrationSqlGenerator());
    
  2. 如果您是从代码生成脚本(就像我一样),请在代码中添加配置程序集的位置添加类似的代码行:

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