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

При добавлении нового столбца, не допускающего значения NULL, в таблицу с помощью первой миграции кода автоматически создается значение по умолчанию. Это имеет смысл, поскольку существующие строки должны иметь значение для нового столбца (поскольку оно не может быть нулевым). Это нормально, но после того, как это значение будет присвоено везде, оно мне больше не нужно. Это значение не может быть обнулено, потому что я хочу убедиться, что значение всегда вставляется явно. Если по умолчанию все равно '' или 0, тогда у меня будут волшебные строки. Так или иначе, я могу найти решение, которое меня не в восторге, но оно работает.

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

public override void Up()
{
    AddColumn("dbo.SomeTable", "NewColumn", c => c.Int(nullable: false));
    Sql(Helpers.DropDefaultConstraint("dbo.SomeTable", "NewColumn"));
}

...

public static string DropDefaultConstraint(string table, string column)
{
    return string.Format(@"
        DECLARE @name sysname

        SELECT @name = dc.name
        FROM sys.columns c
        JOIN sys.default_constraints dc ON dc.object_id = c.default_object_id
        WHERE c.object_id = OBJECT_ID('{0}')
        AND c.name = '{1}'

        IF @name IS NOT NULL
            EXECUTE ('ALTER TABLE {0} DROP CONSTRAINT ' + @name)
        ",
        table, column);
}

Плюсы: после того, как вспомогательный метод реализован, мне просто нужно добавить одну простую строку, чтобы снять ограничение. ПРОТИВ: кажется ненужным создавать индекс только для его удаления.

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

public override void Up()
{
    AddColumn("dbo.SomeTable", "NewColumn", c => c.Int());
    Sql("UPDATE dbo.SomeTableSET NewColumn= 1");
    AlterColumn("dbo.SomeTable", "NewColumn", c => c.Int(nullable: false));
}

ЗА: кажется проще/чище

ПРОТИВ: Приходится временно изменять мои ограничения (я предполагаю, что это выполняется в транзакции, и поэтому мы не должны допускать неверные данные). Обновление может быть медленным на больших таблицах.

Какой метод предпочтительнее? Или есть лучший способ, который мне не хватает?

Примечание. Я продемонстрировал случай, когда вы добавляете и очищаете определения столбцов за один раз. Если вы просто очищаете значения по умолчанию от предыдущих миграций, второй подход бесполезен.


person Dan Friedman    schedule 03.06.2012    source источник
comment
Решение 1 отлично работает для меня. Лучшее, что я нашел до сих пор. Просто хочу добавить, что вы можете явно указать значение по умолчанию вместо того, чтобы полагаться на EF, чтобы предоставить его: c.String(nullable: false, defaultValue: n/a) , например. В случае, если вы хотите обрабатывать старые данные контролируемым образом.   -  person Daniel Hillebrand    schedule 27.03.2018


Ответы (2)


Лично я не вижу ничего плохого в первом подходе. Да, вам нужно создать ограничение по умолчанию, чтобы добавить ненулевой столбец в непустой набор данных. И да, вы должны удалить его впоследствии, если вам нужно убедиться, что новый столбец всегда добавляется явно в будущем, в соответствии с вашими требованиями.

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

Однако я мог бы рассмотреть небольшую модификацию: я мог бы создать ограничение в SQL, чтобы избежать суеты поиска имени по умолчанию, назначенного движком, что-то вроде этого:

Sql("ALTER TABLE tablename
  ADD columnname type NOT NULL
  CONSTRAINT DF_tablename_columnname DEFAULT defaultvalue");

(Можно переписать для использования вспомогательной функции.)

Тогда удаление ограничения будет таким же тривиальным, как выполнение одного оператора ALTER TABLE:

Sql("ALTER TABLE tablename
  DROP CONSTRAINT DF_tablename_columnname");

(Опять же, здесь можно легко использовать вспомогательную функцию.)

Но все это может быть разработчиком T-SQL во мне, говорящим громче, чем разработчиком C#. Таким образом, вы вполне можете использовать код, подобный приведенному вами примеру.

person Andriy M    schedule 03.06.2012
comment
Спасибо за подтверждение моих мыслей. Я рассматривал возможность создания ограничения самостоятельно, но предпочитаю использовать AddColumn, так как его легко и последовательно читать, а также он допускает некоторые другие ограничения, такие как уникальность. Я решил придерживаться варианта 1. Спасибо. - person Dan Friedman; 09.06.2012

Решение с использованием кода EF сначала для удаления ограничения значения по умолчанию из столбца. Если столбцы больше не имеют значений NULL [Not Null], вы можете использовать приведенный ниже подход Code First, чтобы удалить приведенный ниже сценарий SQL из вашей таблицы. Установите defaultValueSql: null в файле миграции.

Удалить ниже Ограничение значения столбца по умолчанию ----> ALTER TABLE [dbo].[Attachments] ADD DEFAULT ('00000000-0000-0000-0000-000000000000') FOR [DocumentUniqueId]

Сначала примените этот код C# ---- ›

protected override void Up(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.AlterColumn<Guid>(
                name: "ColumnName",
                schema: "dbo",
                table: "TableName",
                type: "uniqueidentifier",
                nullable: false,
                **defaultValueSql: null,**
                oldClrType: typeof(Guid),
                oldType: "uniqueidentifier");
        }

Я ничего не добился, поэтому я публикую его, чтобы помочь другим, полезным для очистки базы данных.

person Vaibhav.Inspired    schedule 26.05.2020