entity-framework - 如何覆盖 MigratorScriptingDecorator 生成的 SQL 脚本

标签 entity-framework entity-framework-4

首先使用实体​​框架 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”语句,以便每个迁移都在一个单独的批处理中,这将解决问题。

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

最佳答案

所以随着ILSpy的广泛使用以及 the answer to this question 中的一些指针我找到了一个方法。

有兴趣的可以往下看。

问题
SqlServerMigrationSqlGenerator是最终负责创建 SQL 语句的类,这些 SQL 语句在使用 -Script 时针对目标数据库执行或编写脚本。在包管理器控制台或使用 MigratorScriptingDecorator 时切换.

作品

检查 SqlServerMigrationSqlGenerator 中的 Generate 方法负责 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");
    }
}

现在要强制迁移使用您的自定义生成器,您有两个选择:
  • 如果您希望将其集成到包管理器控制台中,请将以下行添加到您的 Configuration类(class):
       SetSqlGenerator("System.Data.SqlClient", 
                       new BatchSqlServerMigrationSqlGenerator());
    
  • 如果您是从代码生成脚本(就像我一样),请在代码中包含配置程序集的位置添加类似的代码行:
    migrationsConfiguration.SetSqlGenerator(DataProviderInvariantName, 
                       new BatchSqlServerMigrationSqlGenerator());
    
  • 关于entity-framework - 如何覆盖 MigratorScriptingDecorator 生成的 SQL 脚本,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10930259/

    相关文章:

    c# - MySql Linq-实体日期比较

    c# - 如何为实体对象上下文设置命令超时

    entity-framework - 每次使用都需要创建/处理的项目的依赖注入(inject)

    C# MVC 3 验证在特定 View 上失败

    asp.net - 如何在EF Code First中使用会籍提供者?

    c# - Entity Framework 导航属性记录从一个记录移动到另一个 "Wait operation timed out"

    entity-framework - 每个类型的 Entity Framework 表 - 仅从基本类型列中选择

    c# - 多次指定固定名称 System.Data.SqlClient 的提供程序

    c# - EntityFramework 中的公用表表达式

    entity-framework - EF4 POCO 一对多导航属性为空