Подтвердить что ты не робот

Перенос EF для изменения типа данных столбцов

У меня есть модель в моем проекте, как показано ниже:

public class Model 
{
    public int Id { get; set; }
    public long FromNo { get; set; }
    public long ToNo { get; set; }
    public string Content { get; set; }
    public long TicketNo { get; set; }
}

Миграция ниже

public override void Down()
{
    AlterColumn("dbo.Received", "FromNo", c => c.Long(nullable: false));
    AlterColumn("dbo.Received", "ToNo", c => c.Long(nullable: false));
    AlterColumn("dbo.Received", "TicketNo", c => c.Long(nullable: false));
}
public override void Up()
{
    AlterColumn("dbo.Received", "FromNo", c => c.String());
    AlterColumn("dbo.Received", "ToNo", c => c.String());
    AlterColumn("dbo.Received", "TicketNo", c => c.String());
}

когда я использую Update-Database, возникает ошибка ниже:

Объект 'DF__Receiv__FromN__25869641' зависит от столбца 'FromNo. ALTER TABLE ALTER COLUMN FromNo не удалось, потому что один или несколько объекты обращаются к этому столбцу.

В этих таблицах нет внешнего ключа или что еще, в чем проблема?

4b9b3361

Ответ 1

У вас есть ограничение по умолчанию для столбца. Вы должны сначала удалить ограничение, а затем изменить свой столбец.

public override void Up()
{
    Sql("ALTER TABLE dbo.Received DROP CONSTRAINT DF_Receiv_FromN__25869641");
    AlterColumn("dbo.Received", "FromNo", c => c.String());
    AlterColumn("dbo.Received", "ToNo", c => c.String());
    AlterColumn("dbo.Received", "TicketNo", c => c.String());
}

Вероятно, вам придется отказаться от ограничений по умолчанию для других столбцов.

Я только что видел комментарий Андрея (знаю - очень поздно), и он прав. Поэтому более надежным подходом было бы использовать что-то вроде:

 DECLARE @con nvarchar(128)
 SELECT @con = name
 FROM sys.default_constraints
 WHERE parent_object_id = object_id('dbo.Received')
 AND col_name(parent_object_id, parent_column_id) = 'FromNo';
 IF @con IS NOT NULL
     EXECUTE('ALTER TABLE [dbo].[Received] DROP CONSTRAINT ' + @con)

Я знаю, что это, вероятно, не помогает OP, но, надеюсь, это помогает кому-то еще, кто сталкивается с этой проблемой.

Ответ 2

static internal class MigrationExtensions
{
    public static void DeleteDefaultConstraint(this IDbMigration migration, string tableName, string colName, bool suppressTransaction = false)
    {
        var sql = new SqlOperation(
            string.Format(@"DECLARE @SQL varchar(1000)
                            SET @SQL='ALTER TABLE {0} DROP CONSTRAINT ['+(SELECT name
                            FROM sys.default_constraints
                            WHERE parent_object_id = object_id('{0}')
                            AND col_name(parent_object_id, parent_column_id) = '{1}')+']';
                            PRINT @SQL;
                            EXEC(@SQL);", tableName, colName)
            )
        {
            SuppressTransaction = suppressTransaction
        };
        migration.AddOperation(sql);
    }
}

public override void Up()
{
    this.DeleteDefaultConstraint("dbo.Received", "FromNo");
    AlterColumn("dbo.Received", "FromNo", c => c.String());
    this.DeleteDefaultConstraint("dbo.Received", "ToNo");
    AlterColumn("dbo.Received", "ToNo", c => c.String());
    this.DeleteDefaultConstraint("dbo.Received", "TicketNo");
    AlterColumn("dbo.Received", "TicketNo", c => c.String());
}

Ответ 3

Это пример изменения существующего столбца на "не null", который уже имеет ограничение внешнего ключа. Имя столбца - "FKColumnName" в таблице "SubTable", и оно ссылается на столбец "Id" в таблице "MainTable".

Вверх script:

После того, как столбец сделан "не нулевым", индекс и внешний ключ сначала удаляются, а затем снова создаются.

Вниз script:

Здесь шаги идентичны, за исключением того, что столбец снова становится нулевым.

public partial class NameOfMigration : DbMigration
{
    public override void Up()
    {
        DropForeignKey("dbo.SubTable", "FKColumnName", "dbo.MainTable");
        DropIndex("dbo.SubTable", new[] { "FKColumnName" });

        AlterColumn("dbo.SubTable", "FKColumnName", c => c.Int(nullable: false));

        CreateIndex("dbo.SubTable", "FKColumnName");
        AddForeignKey("dbo.SubTable", "FKColumnName", "dbo.MainTable", "Id");
    }

    public override void Down()
    {
        DropForeignKey("dbo.SubTable", "FKColumnName", "dbo.MainTable");
        DropIndex("dbo.SubTable", new[] { "FKColumnName" });

        AlterColumn("dbo.SubTable", "FKColumnName", c => c.Int(nullable: true));

        CreateIndex("dbo.SubTable", "FKColumnName");
        AddForeignKey("dbo.SubTable", "FKColumnName", "dbo.MainTable", "Id");
    }
}

Ответ 4

Лучше всего решить проблему навсегда.

Вы можете реализовать пользовательский класс генератора sql, полученный из SqlServerMigrationSqlGenerator, из пространства имен System.Data.Entity.SqlServer:

using System.Data.Entity.Migrations.Model;
using System.Data.Entity.SqlServer;

namespace System.Data.Entity.Migrations.Sql{
    internal class FixedSqlServerMigrationSqlGenerator : SqlServerMigrationSqlGenerator {
        protected override void Generate(AlterColumnOperation alterColumnOperation){
            ColumnModel column = alterColumnOperation.Column;
            var sql = String.Format(@"DECLARE @ConstraintName varchar(1000);
            DECLARE @sql varchar(1000);
            SELECT @ConstraintName = name   FROM sys.default_constraints
                WHERE parent_object_id = object_id('{0}')
                AND col_name(parent_object_id, parent_column_id) = '{1}';
            IF(@ConstraintName is NOT Null)
                BEGIN
                set @sql='ALTER TABLE {0} DROP CONSTRAINT [' + @ConstraintName+ ']';
            exec(@sql);
            END", alterColumnOperation.Table, column.Name);
                this.Statement(sql);
            base.Generate(alterColumnOperation);
            return;
        }
        protected override void Generate(DropColumnOperation dropColumnOperation){
            var sql = String.Format(@"DECLARE @SQL varchar(1000)
                SET @SQL='ALTER TABLE {0} DROP CONSTRAINT [' + (SELECT name
                    FROM sys.default_constraints
                    WHERE parent_object_id = object_id('{0}')
                    AND col_name(parent_object_id, parent_column_id) = '{1}') + ']';
            PRINT @SQL;
                EXEC(@SQL); ", dropColumnOperation.Table, dropColumnOperation.Name);

                    this.Statement(sql);
            base.Generate(dropColumnOperation);
        }
    }
}

и установите эту конфигурацию:

internal sealed class Configuration : DbMigrationsConfiguration<MyDbContext>
{
    public Configuration()
    {
        AutomaticMigrationsEnabled = true;

        SetSqlGenerator("System.Data.SqlClient", new FixedSqlServerMigrationSqlGenerator ());
    }
    ...
}

Ответ 5

У меня была эта проблема со значением по умолчанию нулевого ограничения для целочисленного столбца.

В моем случае я решил это, переключившись с Entity Framework 6.1.x на EF 6.2.0.

Существует известная ошибка в EF до 6.2, которая означает, что EF иногда не обрабатывает эти типы ограничений автоматически при изменении столбцов. Эта ошибка описана в официальном репозитории EF github здесь, Бриселам описывает проблему следующим образом:

При добавлении столбцов NOT NULL мы синтезируем значение по умолчанию для любого существующие строки. Похоже, наша логика отбросить ограничения по умолчанию перед ALTER COLUMN это не учитывается.

Фикс для исправления этой проблемы можно найти здесь.

Ответ 6

Если вы используете EF:

  • Удалить папку миграции и базу данных
  • enable-migrations
  • add-migration initial
  • update-database

Хотя это решение удалит все текущие элементы в базе данных. Если это не ваше намерение, я бы предложил один из других ответов.