6

在删除列的迁移操作期间,如何在尝试删除列之前生成 SQL 来检查列的存在?

对于删除列操作,实体框架当前会生成这样的 sql 来删除列:

// Migration Operation:
DropColumn("dbo.Table", "Column");

// TSQL generated:
// Dependency management logic ...
ALTER TABLE [dbo].[Table] DROP COLUMN [Column]

如何更改 SQL 以首先检查列的存在:

// Migration Operation:
DropColumn("dbo.Table", "Column");

// TSQL desired:
IF EXISTS (SELECT * FROM sys.columns WHERE object_id = Object_id('dbo.Table') AND name = 'Column')
BEGIN
    // Dependency management logic ...
    ALTER TABLE [dbo].[Table] DROP COLUMN [Column]
END

我知道可以通过继承来自定义迁移 SQL SqlServerMigrationSqlGenerator。我这样做的尝试未能将默认的删除列逻辑包装在一个IF块中。请参见下面的示例:

public class CustomSqlServerMigrationSqlGenerator: SqlServerMigrationSqlGenerator
{
    /// <summary>
    /// Drop column only if it exists.
    /// </summary>
    protected override void Generate(System.Data.Entity.Migrations.Model.DropColumnOperation dropColumnOperation)
    {
        using (var writer = Writer())
        {
            writer.WriteLine(
              "IF EXISTS (SELECT * FROM sys.columns WHERE object_id = Object_id('{0}') AND name = '{1}')",
              dropColumnOperation.Table,
              dropColumnOperation.Name);
            writer.WriteLine("BEGIN");
            Statement(writer);
        }

        // Default drop column logic
        base.Generate(dropColumnOperation);

        using (var writer = Writer())
        {
            writer.WriteLine("END");
            Statement(writer);
        }
    }
}

资料来源:

4

1 回答 1

2

如果您CustomSqlServerMigrationSqlGenerator在执行过程中正确配置了 thenUpdate-Database您应该会遇到以下错误消息:

System.Data.SqlClient.SqlException (0x80131904): Incorrect syntax near 'BEGIN'.

问题是您已经构建并执行了 3 个部分语句,默认情况下调用作为单个批处理操作执行,并且您的批处理在实际的DropColumn语句和End包含在语句中Statement()之前是无效的语法。

由于基础实现不允许我们通过文本编写器(我们需要的方法被标记protected),我们被迫做的是完全忽略基础实现:

/// <summary>
/// Drop column only if it exists.
/// </summary>
/// <remarks>This helps when we're stuffed up a previous migration or someone has already executed the drop in the DB direct.</remarks>
protected override void Generate(System.Data.Entity.Migrations.Model.DropColumnOperation dropColumnOperation)
{
    using (var writer = Writer())
    {
        writer.WriteLine(
          "IF EXISTS (SELECT * FROM sys.columns WHERE object_id = Object_id('{0}') AND name = '{1}')",
          dropColumnOperation.Table,
          dropColumnOperation.Name);
        writer.WriteLine("BEGIN");

        // Base Implementation, increase the indentation
        writer.Indent++;
        writer.WriteLine("ALTER TABLE {0}", Quote(dropColumnOperation.Table));
        writer.WriteLine("DROP COLUMN {0}", Quote(dropColumnOperation.Name));
        writer.Indent--;

        writer.WriteLine("END");

        Statement(writer);
    }
}

如果您没有看到当前的错误,可能CustomSqlServerMigrationSqlGenerator是未正确注册,请确保您已在 Configuration.cs 的 Configuration 类的构造函数中设置它,例如:

public Configuration()
{
    AutomaticMigrationsEnabled = false;
    AutomaticMigrationDataLossAllowed = false;
    // Register the Customized SQL Generator to use
    this.SetSqlGenerator("System.Data.SqlClient", new CustomSqlServerMigrationSqlGenerator());
}

如果您需要调试此过程,请按照与此类似的情况的这篇文章中的建议进行操作,您可以在生成器类的构造函数中放置一个断点:

public class CustomSqlServerMigrationSqlGenerator: SqlServerMigrationSqlGenerator
{
    public CustomSqlServerMigrationSqlGenerator()
        : base()
    {
        if (!System.Diagnostics.Debugger.IsAttached)
            System.Diagnostics.Debugger.Launch();
    }

    ...
}
于 2020-08-23T06:40:20.583 回答