Я использую 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.
Однако в идеале я должен просто избавиться от этой ошибки, я удалил столбец, ограничения, а также таблицу, а затем и всю базу данных. Но все та же ошибка.
@ElyasEsna, One Customer One BankAccount, поэтому идентификатор в сущности клиента можно использовать как FK и первичный ключ в сущности BankAccount.
похоже, проблема github.com/aspnet/EntityFrameworkCore/issues/7444, которая является открытой проблемой в ядре EF.
Для будущих ответчиков: люди продолжают накапливать «решения» на этот вопрос, все они сводятся примерно к одному и тому же, и ни одно из них не подходит для производственной среды, которая не допускает потери данных и имеет внешние ключи к первичному ключу. Если у вас есть твердое решение, учитывающее это, опубликуйте его, в противном случае подумайте дважды, прежде чем добавлять больше шума.





Эта ошибка возникает, когда вы пытаетесь изменить или изменить таблицу, которая уже существует, когда вы хотите изменить схему или таблицу, которая уже существует, и ядро EF не поддерживает ее, но требует ручных действий. вот что вы можете с этим сделать:
У меня возникла эта проблема, когда я попытался сменить модель с public byte Id {get; set;} на public int Id {get; set;}.
Чтобы решить эту проблему, я сделал следующее:
Remove-Migration -Project <target_project> в консоли диспетчера пакетов.ModelSnapshot и вставьте их в свою ветку (перезапишите их осторожно!).add-migration <migration_name> в консоли диспетчера пакетовupdate-database в консоли диспетчера пакетовЯ могу решить это таким образом, потому что мой код не был в производственной среде. Возможно, вам придется столкнуться с другими сложными проблемами, если модель уже существует.
Хотя эта стратегия совершенно верна, если вы работаете в среде разработки, она становится проблематичной, если вы работаете в предпроизводственной среде.
Я столкнулся с той же проблемой, и я решил ее за два шага и две миграции:
Шаг 1
Шаг 2
Работал отлично
Работает только в сверхпростом сценарии без внешних ключей. Не совсем подходящее решение.
Этого было достаточно для меня, чтобы преодолеть черту, спасибо
На мой взгляд, запуск EF Migrations против чего-либо, кроме вашей базы данных разработки, вызывает проблемы, поскольку вы, естественно, ограничены тем фактом, что EF-миграции иногда категорически отказываются работать при изменении структуры ваших объектов (изменение первичных ключей и изменение внешних ключей наиболее часто встречающиеся).
В течение многих лет я использовал инструменты, чтобы гарантировать, что схема БД включена в систему контроля версий (в дополнение к миграции EF). Сделайте свои разработки, чтобы изменить свою базу данных разработчиков (где данные не важны), создайте несколько миграций, но затем используйте инструменты для их объединения в сценарий развертывания БД.
Вот краткое описание того, что я бы сделал в этом случае: -
BankAccountBankAccount с его исправленным определением.Реальность такова, что если у вас есть производственные данные, структуру которых вы хотите радикально изменить с помощью подхода, основанного на коде, это, вероятно, плохо кончится для вас, если вы не поймете и не решите миграцию данных из одной структуры в другую.
«Я много лет пользовался инструментами ...» - расскажите, пожалуйста, какими инструментами вы пользовались?
ApexSQL - мой предпочтительный инструмент для сравнения как схемы БД, так и данных БД, чтобы помочь процессу развертывания. У них есть различные варианты покупки и полнофункциональная бесплатная пробная версия. У Red Gate есть аналогичное предложение под названием «SQL Compare».
Любой ответ (даже если команда MS Docs Team мне говорит), который сначала вращается вокруг кода с миграциями, предназначен для студенческих проектов и учебных пособий. В реальном мире с живыми инстансами БД это ужасная шутка. То, что @ Cueball6118 ответил выше, является реальным ответом (используете ли вы Apex и т. д., Visual Studio DB Comparison или просто сценарии SQL). Мы поддерживаем экземпляры Live DB (MSSQL) и советуем новичкам отучиться от этих практических занятий, чтобы избежать бессонных ночей на работе. Даже идеально выглядящая миграция может привести к недопустимому простою в реальных средах.
Мне пришлось:
Сделанный
Работает только в сверхпростом сценарии без внешних ключей. Не совсем жизнеспособное решение.
Нет, внешние ключи уже были. Вот почему возникла проблема.
Что ты имеешь в виду? Эти 4 шага не работают с внешними ключами таблицы, которую вы отбрасываете.
В моем случае таблица 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-обновление-база данных
наслаждайся этим
Я столкнулся с той же проблемой (в моем случае у меня не было данных в таблицах), и я решил ее таким образом (это не правильный способ, но у меня это сработало):
TL; DR: Пусть 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);
Это будет отражать отношение «один ко многим», и это нормально.
Теперь, чтобы ответить на вопрос, хотите ли вы все еще отбросить свойство:
(но это не решит проблему внешнего ключа исходного вопроса).
Во-первых, просто напомним: чтобы изменить свойство идентификации, менеджер БД (mysql, sqlserver и т. д.) Всегда будет просить вас удалить и воссоздать таблицу, потому что это поле является сердцем таблицы. Итак, вам нужно обмануть EF, чтобы он нашел обходной путь для вас.
Добавьте второй элемент Id в класс модели с очевидным именем, например duplicateId.
public class BankAccount
{
public int Id { get; set; }
public int duplicateId { get; set; }
...
}
ЭТО УЮЛКА;)
В том классе, в котором вы реализовали интерфейс DbContext, добавьте следующий метод, в котором вы указываете ef, в каком поле вы хотите использовать первичный ключ:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<BankAccount>().HasKey(x => new { x.duplicateId });
}
Добавьте новую миграцию с $ 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");
...
}
Затем выполните обновление базы данных с помощью $ dotnet ef database update (эти команды могут отличаться, используйте любой синтаксис, который вы уже использовали ранее).
(НЕОБЯЗАТЕЛЬНО) Если вам нужно сохранить исходные идентификаторы, убедитесь, что они сохранились, или просто выполните «грязное» обновление таблицы, чтобы скопировать данные из поля Id в duplicateId.
Теперь исходное поле Id можно удалить или обновить, поэтому просто удалите исходное поле из модели:
public class BankAccount
{
public int duplicateId { get; set; }
...
}
Если вы все еще пытаетесь принудительно запустить исходную команду, которая связывает идентификатор BankAccount с идентификатором клиента, она должна сработать, если вы запустите команду на этом этапе, но, пожалуйста, не делайте этого.
И добавьте новую миграцию с $ dotnet ef migrations add ModelIdChange2, а затем выполните обновление базы данных с помощью $ dotnet ef database update, при котором будет удален исходный столбец Id и останется duplicateId в качестве первичного ключа.
Теперь модель выглядит почти как оригинальная, но с новым столбцом идентификаторов вы можете оставить это так или просто переименовать поле обратно с duplicateId в Id в классе BankAccount следующим образом:
public class BankAccount
{
public int Id { get; set; }
...
}
и сделайте $ dotnet ef migrations add ModelIdChange3, а затем выполните обновление базы данных с помощью $ dotnet ef database update.
Для таких ленивых, как я: вы хотите изменить тип данных столбца первичного ключа "Идентификатор" с int на Гид в таблице с именем "Переводы", как в моем случае.
migrationBuilder.AlterColumn<Guid>( name: "Id", table: "Translations", type: "uniqueidentifier", nullable: false, oldClrType: typeof(int), oldType: "int") OldAnnotation("SqlServer:Identity", "1, 1");
Вы можете удалить или прокомментировать это
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 на случай, если вы захотите отменить миграцию.
Как это отвечает на вопрос?
У меня была аналогичная проблема, когда я менял компонент реляционной навигации в конфигурации таблицы с WithMany на WithRequiredDependent. Платформа Entity Framework хотела удалить индекс и воссоздать столбец, хотя в базе данных ничего не должно было измениться.
Чтобы исправить это, я перекомпоновал последнюю миграцию, которая позволила сущности принять изменение без создания новой миграции. Вы можете перекомпилировать последнюю миграцию, отменив миграцию из целевой базы данных и повторно запустив сценарий Add-Migration для последней миграции с тем же именем миграции.
entity.ToTable("BankAccount2")Add-Migration BankAccountTempChangesUpdate-Databaseentity.ToTable("BankAccount")Add-Migration BankAccountOkUpdate-Database
что вы имеете в виду под «... я понял, что наличие Id и CustomerId - это накладные расходы, поскольку их взаимно однозначное отношение»? Id - это первичный ключ, а CustomerId - внешний ключ, так какие накладные расходы здесь?