Измените свойство IDENTITY столбца, столбец необходимо удалить и создать заново

Я использую EF Core 2.1

Это было мое первоначальное определение модели.

public class Customer //Parent
{
    public int Id { get; set; }

    public string Name { get; set; }

    public string Email { get; set; }

    public BankAccount BankAccount { get; set; }

}


public class BankAccount
{
    public int Id { get; set; }

    public string Branch { get; set; }

    public string AcntNumber { get; set; }

    public DateTime CreatedDate { get; set; }

    public int CustomerId { get; set; }

    public Customer Customer { get; set; }

}

Но я понял, что наличие Id и CustomerId является накладными расходами, поскольку их отношение «один к одному», я могу обновить определение модели BankAccount, как показано ниже.

public class BankAccount
{
    public int Id { get; set; }

    public string Branch { get; set; }

    public string AcntNumber { get; set; }

    public DateTime CreatedDate { get; set; }

    public Customer Customer { get; set; }

}

В то время как в классе DbContext определена основная сущность, как показано ниже.

HasOne(b => b.Customer).WithOne(c => c.BankAccount).HasForeignKey<BankAccount>(f => f.Id);

При запуске update-database я получаю следующую ошибку.

System.InvalidOperationException: To change the IDENTITY property of a column, the column needs to be dropped and recreated.

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

что вы имеете в виду под «... я понял, что наличие Id и CustomerId - это накладные расходы, поскольку их взаимно однозначное отношение»? Id - это первичный ключ, а CustomerId - внешний ключ, так какие накладные расходы здесь?

Elyas Esna 21.11.2018 10:47

@ElyasEsna, One Customer One BankAccount, поэтому идентификатор в сущности клиента можно использовать как FK и первичный ключ в сущности BankAccount.

Kgn-web 21.11.2018 10:51

похоже, проблема github.com/aspnet/EntityFrameworkCore/issues/7444, которая является открытой проблемой в ядре EF.

DevilSuichiro 21.11.2018 11:49

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

Gert Arnold 12.07.2021 23:12
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
36
4
51 204
12

Ответы 12

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

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

У меня возникла эта проблема, когда я попытался сменить модель с public byte Id {get; set;} на public int Id {get; set;}. Чтобы решить эту проблему, я сделал следующее:

  1. Удалите все миграции до создания целевой модели с Remove-Migration -Project <target_project> в консоли диспетчера пакетов.
  2. Удалить актуальную базу данных
  3. Если у вас есть некоторые миграции в середине, которые вы не создавали (например, они пришли из другой ветки), скопируйте файлы миграции, а также файл ModelSnapshot и вставьте их в свою ветку (перезапишите их осторожно!).
  4. создать новую миграцию с add-migration <migration_name> в консоли диспетчера пакетов
  5. обновить базу данных с помощью update-database в консоли диспетчера пакетов

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

Хотя эта стратегия совершенно верна, если вы работаете в среде разработки, она становится проблематичной, если вы работаете в предпроизводственной среде.

Falk 12.12.2019 11:25

Я столкнулся с той же проблемой, и я решил ее за два шага и две миграции:

Шаг 1

  1. Отбросьте столбец идентичности.
  2. Прокомментируйте идентификатор в BankAccount и добавьте новый (например, BankAccountId as
    identity, добавьте миграцию и обновление - это отбрасывает id).
  3. Добавьте новый столбец в качестве идентификатора.

Шаг 2

  1. Отбросьте только что добавленный столбец и снова добавьте предыдущий. Комментарий BankAccountId и идентификатор без комментария.
  2. Добавьте миграцию и обновление (это приведет к удалению BankAccountId и добавлению идентификатора в качестве идентификатора).

Работал отлично

Mohammed A. Fadil 21.01.2020 23:29

Работает только в сверхпростом сценарии без внешних ключей. Не совсем подходящее решение.

Gert Arnold 26.09.2020 19:36

Этого было достаточно для меня, чтобы преодолеть черту, спасибо

csharpdude77 14.01.2021 15:26

На мой взгляд, запуск EF Migrations против чего-либо, кроме вашей базы данных разработки, вызывает проблемы, поскольку вы, естественно, ограничены тем фактом, что EF-миграции иногда категорически отказываются работать при изменении структуры ваших объектов (изменение первичных ключей и изменение внешних ключей наиболее часто встречающиеся).

В течение многих лет я использовал инструменты, чтобы гарантировать, что схема БД включена в систему контроля версий (в дополнение к миграции EF). Сделайте свои разработки, чтобы изменить свою базу данных разработчиков (где данные не важны), создайте несколько миграций, но затем используйте инструменты для их объединения в сценарий развертывания БД.

Вот краткое описание того, что я бы сделал в этом случае: -

  1. Убрать (закомментировать) все ссылки на старый класс BankAccount
  2. Создать миграцию и применить к базе данных разработчика
  3. Повторно добавьте класс BankAccount с его исправленным определением.
  4. Создать миграцию и применить к базе данных разработчика
  5. Используйте инструмент сравнения БД (я предпочитаю APEX SQL Diff, но на рынке есть и другие), чтобы создать сценарий развертывания, который объединяет обе миграции.
  6. Протестируйте этот скрипт в своей промежуточной среде (где у вас должны быть данные)
  7. Если проверка прошла успешно, обратитесь в производство

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

«Я много лет пользовался инструментами ...» - расскажите, пожалуйста, какими инструментами вы пользовались?

arantar 03.04.2020 22:57

ApexSQL - мой предпочтительный инструмент для сравнения как схемы БД, так и данных БД, чтобы помочь процессу развертывания. У них есть различные варианты покупки и полнофункциональная бесплатная пробная версия. У Red Gate есть аналогичное предложение под названием «SQL Compare».

Cueball 6118 05.04.2020 23:54

Любой ответ (даже если команда MS Docs Team мне говорит), который сначала вращается вокруг кода с миграциями, предназначен для студенческих проектов и учебных пособий. В реальном мире с живыми инстансами БД это ужасная шутка. То, что @ Cueball6118 ответил выше, является реальным ответом (используете ли вы Apex и т. д., Visual Studio DB Comparison или просто сценарии SQL). Мы поддерживаем экземпляры Live DB (MSSQL) и советуем новичкам отучиться от этих практических занятий, чтобы избежать бессонных ночей на работе. Даже идеально выглядящая миграция может привести к недопустимому простою в реальных средах.

Ajay 19.04.2021 19:08

Мне пришлось:

  1. Прокомментируйте таблицу полностью из кода
  2. Запустите миграцию, которая очистит его от БД
  3. Снова раскомментируйте таблицу с исправленным отображением
  4. Снова запустить миграцию

Сделанный

Работает только в сверхпростом сценарии без внешних ключей. Не совсем жизнеспособное решение.

Gert Arnold 26.09.2020 19:37

Нет, внешние ключи уже были. Вот почему возникла проблема.

Bassel 29.09.2020 13:15

Что ты имеешь в виду? Эти 4 шага не работают с внешними ключами таблицы, которую вы отбрасываете.

Gert Arnold 29.09.2020 19:29

В моем случае таблица SharedBalances переименована в Balances, а ее идентификационный столбец SharedBalancesId переименован в BalanceId. Команды SQL выполняются на SQL Server. Вы также можете попробовать migrationBuilder.Sql (my_sql_command_here)

Я создал миграцию и получил ту же ошибку.

Переименуйте столбец и таблицу с помощью команды TSQL:

EXEC sp_RENAME 'SharedBalances.SharedBalanceId', 'BalanceId', 'COLUMN';

EXEC sp_RENAME 'SharedBalances', 'Balances';

-- Caution: Changing any part of an object name could break scripts and stored procedures.

Прокомментируйте команду RenameTable в своей миграции:

/*
migrationBuilder.RenameTable(
    name: "SharedBalances",
    newName: "Balances");
*/

Прокомментируйте команду AddPrimaryKey в своей миграции:

/*
migrationBuilder.DropPrimaryKey(
    name: "PK_SharedBalances",
    table: "Balances");
migrationBuilder.AddPrimaryKey(
    name: "PK_Balances",
    table: "Balances",
    column: "BalanceId");
*/

Обновите вхождения команд DropForeignKey имени таблицы в вашей миграции:

Из этого....

        migrationBuilder.DropForeignKey(
            name: "FK_SharedBalances_Users_OwnerUserId",
            table: "SharedBalances");

        migrationBuilder.DropPrimaryKey(
            name: "PK_SharedBalances",
            table: "SharedBalances");

К этому:

        migrationBuilder.DropForeignKey(
            name: "FK_SharedBalances_Users_OwnerUserId",
            table: "Balances");

        migrationBuilder.DropPrimaryKey(
            name: "PK_SharedBalances",
            table: "Balances");

Теперь ваша миграция будет работать. Вот как это случилось:

пожалуйста, выполните этот шаг:

1-пожалуйста, сделайте все изменение столбца идентификатора на сервере sql (не в вашей структуре первого объекта кода)

2 комментария изменения столбца идентификаторов в миграции (файл .cs)

3-обновление-база данных

наслаждайся этим

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

  1. Я удалил вручную миграции из проекта EFCore. Я удалил те строки, которые были добавлены из файла _ContextModelSnapshot. (У меня была одна миграция, которая была применена, и одна, которая была создана, но не применялась, так как я получал ошибку - Измените свойство IDENTITY столбца, столбец необходимо удалить и создать заново.)
  2. Я удалил вручную таблицы, которые были созданы в базе данных (при первой миграции)
  3. Я удалил строку в таблице _EFMigrationHistory, которая связана с миграцией, которую я хотел удалить.
  4. Повторно запустить VS
  5. Добавить-Migration NewOneCleanMigration
  6. Обновление базы данных

Вопрос состоит из двух частей:

TL; DR: Пусть EF сделает это за вас.

Во-первых: пусть EF выполняет отношения

Короче говоря, свойство идентификации - это то, что БД использует для управления выделенным столбцом идентификатора, поэтому он не зависит от чего-либо внешнего для идентификации каждой строки, поэтому классу bankAccount необходимо собственное поле идентификатора, чтобы иметь свойство идентификации; теперь, если вы попытаетесь указать EF вручную, как делать отношения, как вы делаете с строкой

    HasOne(b => b.Customer).WithOne(c => c.BankAccount).HasForeignKey<BankAccount>(f => f.Id);

то, что вы делаете, - это переопределение внутренней логики самого EF, и в этом случае вы сообщаете EF, что идентификатор банковского счета - это поле, которое ссылается на клиента, что не так, и поэтому EF пытается удалить IDENTITY из поле id в модели банковского счета, но это только потому, что вы сказали ему, что идентификатор банковского счета должен совпадать с идентификатором клиента.. Думаю, я понимаю, что вы пытаетесь сказать об отношениях «один к одному», но что происходит, когда клиент пытается открыть другой банковский счет? D: Вместо этого вы должны сохранить поле int CustomerId и удалить поле Customer Customer не только потому, что оно чище, но и потому, что EF распознает связь между ними, пока существует класс с именем Customer с полем Id.

   public int CustomerId { get; set; } //keep

   public Customer Customer { get; set; } //delete

Таким образом, клиент может открыть столько счетов в банке, сколько ему заблагорассудится, стол не пострадает, и EF знает, что делать.

Он автоматически сгенерирует соответствующий внешний ключ и добавит его в файл миграции следующим образом:

    migrationBuilder.AddForeignKey(
            name: "FK_BankAccount_Customer_CustomerId",
            table: "BankAccount",
            column: "CustomerId",
            principalTable: "Customer",
            principalColumn: "Id",
            onDelete: ReferentialAction.Cascade);

Это будет отражать отношение «один ко многим», и это нормально.

Теперь, чтобы ответить на вопрос, хотите ли вы все еще отбросить свойство:

Во-вторых: заставить EF удалить свойство Identity

(но это не решит проблему внешнего ключа исходного вопроса).

Во-первых, просто напомним: чтобы изменить свойство идентификации, менеджер БД (mysql, sqlserver и т. д.) Всегда будет просить вас удалить и воссоздать таблицу, потому что это поле является сердцем таблицы. Итак, вам нужно обмануть EF, чтобы он нашел обходной путь для вас.

  1. Добавьте второй элемент Id в класс модели с очевидным именем, например duplicateId.

    public class BankAccount
    {
        public int Id { get; set; }
        public int duplicateId { get; set; }
        ...
    }
    
  2. ЭТО УЮЛКА;)
    В том классе, в котором вы реализовали интерфейс DbContext, добавьте следующий метод, в котором вы указываете ef, в каком поле вы хотите использовать первичный ключ:

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<BankAccount>().HasKey(x => new { x.duplicateId });
    }
    
  3. Добавьте новую миграцию с $ dotnet ef migrations add ModelIdChange1, поскольку при этом изменяется первичный ключ таблицы, и метод миграции Up должен выглядеть примерно так:

    protected override void Up(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.DropPrimaryKey(
            name: "PK_BankAccount",
            table: "BankAccountTableName");
    
        migrationBuilder.AddColumn<int>(
            name: "duplicateId",
            table: "BankAccountTableName",
            type: "int",
            nullable: false,
            defaultValue: 0)
            .Annotation("SqlServer:Identity", "1, 1");
    
        migrationBuilder.AddPrimaryKey(
            name: "PK_BankAccount",
            table: "BankAccountTableName",
            column: "duplicateId");
    ...
    }
    
  4. Затем выполните обновление базы данных с помощью $ dotnet ef database update (эти команды могут отличаться, используйте любой синтаксис, который вы уже использовали ранее).

  5. (НЕОБЯЗАТЕЛЬНО) Если вам нужно сохранить исходные идентификаторы, убедитесь, что они сохранились, или просто выполните «грязное» обновление таблицы, чтобы скопировать данные из поля Id в duplicateId.

  6. Теперь исходное поле Id можно удалить или обновить, поэтому просто удалите исходное поле из модели:

    public class BankAccount
    {
        public int duplicateId { get; set; }
        ...
    }
    

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

  1. И добавьте новую миграцию с $ dotnet ef migrations add ModelIdChange2, а затем выполните обновление базы данных с помощью $ dotnet ef database update, при котором будет удален исходный столбец Id и останется duplicateId в качестве первичного ключа.

  2. Теперь модель выглядит почти как оригинальная, но с новым столбцом идентификаторов вы можете оставить это так или просто переименовать поле обратно с duplicateId в Id в классе BankAccount следующим образом:

    public class BankAccount
    {
        public int Id { get; set; }
        ...
    }
    

    и сделайте $ dotnet ef migrations add ModelIdChange3, а затем выполните обновление базы данных с помощью $ dotnet ef database update.

Для таких ленивых, как я: вы хотите изменить тип данных столбца первичного ключа "Идентификатор" с int на Гид в таблице с именем "Переводы", как в моем случае.

  1. Ваша сгенерированная миграция в этом случае
migrationBuilder.AlterColumn<Guid>(
     name: "Id",
     table: "Translations",
     type: "uniqueidentifier",
     nullable: false,
     oldClrType: typeof(int),
     oldType: "int")
     OldAnnotation("SqlServer:Identity", "1, 1");

Вы можете удалить или прокомментировать это

  1. Из ошибки обновления базы данных System.InvalidOperationException: чтобы изменить свойство IDENTITY столбца, столбец необходимо удалить и воссоздать заново..
  2. Мы также знаем, что мы не можем отбросить столбец, не сняв сначала ограничение первичного ключа. Наша новая миграция становится
migrationBuilder.DropPrimaryKey(
    name: "PK_Translations",
    table: "Translations");
  migrationBuilder.DropColumn(
    name: "Id",
    table: "Translations");
  migrationBuilder.AddColumn<Guid>(
    name: "Id",
    table: "Translations",
    type: "uniqueidentifier",
    nullable: false);

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

Как это отвечает на вопрос?

Gert Arnold 03.06.2021 09:36

У меня была аналогичная проблема, когда я менял компонент реляционной навигации в конфигурации таблицы с WithMany на WithRequiredDependent. Платформа Entity Framework хотела удалить индекс и воссоздать столбец, хотя в базе данных ничего не должно было измениться.

Чтобы исправить это, я перекомпоновал последнюю миграцию, которая позволила сущности принять изменение без создания новой миграции. Вы можете перекомпилировать последнюю миграцию, отменив миграцию из целевой базы данных и повторно запустив сценарий Add-Migration для последней миграции с тем же именем миграции.

  1. В таблице нет важных данных
  • Переименуйте таблицу entity.ToTable("BankAccount2")
  • Добавить Выполнить миграцию Add-Migration BankAccountTempChanges
  • Обновить базу Update-Database
  • Переименовать стол обратно в entity.ToTable("BankAccount")
  • Добавить Выполнить миграцию Add-Migration BankAccountOk
  • Обновите базу еще раз Update-Database
  1. В таблице есть данные, которые нельзя потерять
  • Применить решение из ответа @Hani

Другие вопросы по теме