【问题标题】:How can I override SQL scripts generated by MigratorScriptingDecorator如何覆盖 MigratorScriptingDecorator 生成的 SQL 脚本
【发布时间】:2012-06-07 10:46:09
【问题描述】:

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

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

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

变量名是@var0。

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

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

Update-Database -Script

以下是生成的脚本中有问题的 sn-ps:

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

任何人有任何想法如何做到这一点,或者如果我叫错了树,那么也许你可以提出更好的方法?

【问题讨论】:

    标签: entity-framework entity-framework-4


    【解决方案1】:

    因此,通过大量使用ILSpy 和the answer to this question 中的一些指针,我找到了一种方法。

    下面有兴趣的人的详细信息。

    问题

    SqlServerMigrationSqlGenerator 是最终负责创建 SQL 语句的类,这些语句在使用包管理器控制台中的 -Script 开关或使用 MigratorScriptingDecorator 时对目标数据库执行或脚本化。

    工作

    检查SqlServerMigrationSqlGenerator 中负责DROP COLUMN 的Genearate 方法,它看起来像这样:

    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());
      

    【讨论】:

    • 您认为为正常的直接表插入生成的 sql 可能有类似的解决方法吗?
    猜你喜欢
    • 1970-01-01
    • 2020-04-17
    • 2021-11-23
    • 2013-10-26
    • 2015-02-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多