【问题标题】:Entity Framework 6: Migration Drop column if exists实体框架 6:迁移删除列(如果存在)
【发布时间】:2017-10-25 13:26:30
【问题描述】:

在删除列的迁移操作期间,如何生成 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

我知道可以通过继承SqlServerMigrationSqlGenerator 来自定义迁移SQL。我这样做的尝试未能将默认的删除列逻辑包装在 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);
        }
    }
}

来源:

【问题讨论】:

  • 你通常不需要这样做,除非你的迁移搞砸了。 EF 会从上次迁移到当前迁移进行模式比较。如果这是 SQL Server 2016,则有一个新的 Drop If Exists statement,您可以将其编码到 Sql() 命令中。
  • @SteveGreene 它用于管理团队环境中的迁移。我们使用相同的迁移并在进行更改时重新构建它。用例是为了简化开发中的重新脚手架。一旦迁移达到生产状态,它就不会改变。问题集中在自定义sql迁移生成,而不是像Sql()这样手动添加迁移操作。当然还有其他方法可以在团队环境中管理迁移,但这些方法不符合我们的需求:msdn.microsoft.com/en-us/library/dn481501(v=vs.113).aspx
  • 在第二篇文章中使用protected override void Generate(MigrationOperation migrationOperation) 代替了同名的方法,但使用了DropColumnOperation 参数:if (operation as DropColumnOperation)
  • @SteveGreene 您是对的,但是当使用 Code First From Database 从现有数据库启动时,也很容易弄乱他的数据库或命名约定错误。 EF也远非完美。 EF 无法处理现有数据库的所有情况。

标签: entity-framework entity-framework-6 ef-code-first entity-framework-migrations sql-generation


【解决方案1】:

如果您正确配置了CustomSqlServerMigrationSqlGenerator,那么在执行Update-Database 期间,您应该会遇到以下错误消息:

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

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

由于基础实现不允许我们通过文本编写器(我们需要的方法标记为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());
}

如果你需要调试这个过程,按照advice in this post这个类似的场景,你可以在你的生成器类的构造函数中设置一个断点:

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

    ...
}

【讨论】:

  • 您将如何处理需要首先删除的依赖项?
猜你喜欢
  • 2015-06-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-09-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多