如何覆盖MigratorScriptingDecorator生成的SQL脚本

时间:2012-06-07 10:46:09

标签: entity-framework entity-framework-4

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

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

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

变量名称为 @ var0

当应用多个迁移时,以及当至少两个迁移导致默认约束被删除时,似乎会发生这种情况。

在生成脚本表单代码和使用Package Manager控制台命令时都会出现问题:

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”语句,以便每次迁移都在一个单独的批处理中,这将解决问题。

任何人都有任何想法如何做到这一点,或者如果我咆哮错误的树然后你可以建议一个更好的方法?

1 个答案:

答案 0 :(得分:4)

因此,在ILSpy中广泛使用了the answer to this question和一些指针,我找到了一种方法。

以下是有兴趣的人的详细信息。

<强>问题

SqlServerMigrationSqlGenerator是最终负责创建对目标数据库执行的SQL语句或在Package Manager控制台中使用-Script开关时使用{{1 }}

<强>巷道

检查负责MigratorScriptingDecorator的{​​{1}}中的Genearate方法,它看起来像这样:

SqlServerMigrationSqlGenerator

您可以看到它跟踪所使用的变量名称,但这似乎只能在批次中跟踪,即单次迁移。因此,如果一个migratin包含多个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变量。

不生成脚本时不会遇到任何问题,因为每个语句都是针对数据库立即执行的(我使用SQL事件探查器检查过)。

如果您生成了一个SQL脚本,并希望尽管遇到问题仍然按原样运行它。

<强>解决方案

我创建了一个新的DROP COLUMN,继承自_variableCounter,如下所示(请注意,您需要BatchSqlServerMigrationSqlGenerator):

SqlServerMigrationSqlGenerator

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

  1. 如果您希望将其集成到Package Manager控制台中,请将以下行添加到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. 如果您是从代码生成脚本(就像我一样),请在代码中添加类似的代码行:

    Configuration