Добавление внешнего ключа с первой миграцией кода - программирование
Подтвердить что ты не робот

Добавление внешнего ключа с первой миграцией кода

У меня есть ошибка при попытке обновить мою базу данных после добавления миграции.

Вот мои классы перед добавлением-переносом

public class Product
{
    public Product() { }

    public int ProductId { get; set; } 
    public string Name { get; set; }
    public decimal Price { get; set; }
    public bool Istaxable { get; set; }
    public string DefaultImage { get; set; }
    public IList<Feature> Features { get; set; }
    public IList<Descriptor> Descriptors { get; set; }
    public IList<Image> Images { get; set; }
    public IList<Category> Categories { get; set; }
}


public class Feature
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string Description { get; set; }

}

Теперь я хотел добавить внешний ключ в свой класс Feature и реорганизовать классы следующим образом:

public class Product
{
    public Product() { }

    public int ProductId { get; set; } 
    public string Name { get; set; }
    public decimal Price { get; set; }
    public bool Istaxable { get; set; }
    public string DefaultImage { get; set; }
    public IList<Feature> Features { get; set; }
    public IList<Descriptor> Descriptors { get; set; }
    public IList<Image> Images { get; set; }
    public IList<Category> Categories { get; set; }
}

public class Feature
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string Description { get; set; }
    public string Image { get; set; }
    public string VideoLink { get; set; }

    public int ProductId { get; set; }
    public Product Product { get; set; }
}

Я добавил миграцию с помощью команды Add-Migration. Я добавил команду Update-Database, и вот что я получил:

Оператор ALTER TABLE противоречил ограничениям FOREIGN KEY "FK_dbo.ProductFeatures_dbo.Products_ProductId". Конфликт произошел в базе данных "CBL", таблица "dbo.Products", столбец "ProductId"

Что я могу сделать, чтобы решить эту проблему и вернуть мои изменения в нормальное состояние?

4b9b3361

Ответ 1

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

Первая миграция

  • Добавить новое свойство в ваш класс как тип с нулевым значением (например, int?)

    public class MyOtherEntity
    {
        public int Id { get; set; }
    }
    
    public class MyEntity
    {
        ...
        // New reference to MyOtherEntity
        public int? MyOtherEntityId { get; set; }
        ...
    }
    
  • Создайте миграцию. ПРИМЕЧАНИЕ. Имя миграции не важно, но что-то вроде "AddBlogPosts1" упрощает чтение.

    > add-migration AddMyEntityMyOtherEntity1
    
  • Это должно поднять миграцию, которая выглядит следующим образом:

    public partial class AddMyTableNewProperty1 : DbMigration
    {
        public override void Up()
        {
            AddColumn("dbo.MyEntity", "MyOtherEntityId", c => c.Int());
        }
        public override void Down()
        {
            DropColumn("dbo.MyEntity", "MyOtherEntityId");
        }
    }
    
  • Теперь вручную измените сгенерированную миграцию, чтобы добавить значение по умолчанию для нового поля. Самый простой случай - когда значение по умолчанию является инвариантным. В случае необходимости вы можете добавить больше логики в SQL. В этом примере предполагается, что все экземпляры MyEntity указывают на тот же экземпляр MyOtherEntity с идентификатором 1.

    public partial class AddMyTableNewProperty1 : DbMigration
    {
        public override void Up()
        {
            AddColumn("dbo.MyEntity", "MyOtherEntityId", c => c.Int());
    
            // ADD THIS BY HAND
            Sql(@"UPDATE dbo.MyEntity SET MyOtherEntityId = 1
                  where MyOtherEntity IS NULL");
        }
        public override void Down()
        {
            DropColumn("dbo.MyEntity", "MyOtherEntityId");
        }
    }
    
  • Обновить базу данных

    > update-database
    

Вторая миграция

  • Вернитесь в свой класс MyEntity и измените новое свойство, чтобы он представлял собой обязательный внешний ключ.

    public class MyEntity
    {
        ...
        // Change the int? to int to make it mandatory
        public int MyOtherEntityId { get; set; }
    
        // Create a reference to the other entity
        public virtual MyOtherEntity MyOtherEntity { get; set; }
        ...
    }
    
  • Создайте еще одну миграцию

    > add-migration AddMyEntityMyOtherEntity2
    
  • Это должно создать миграцию следующим образом:

    public partial class AddMyEntityMyOtherEntity2: DbMigration
    {
        public override void Up()
        {
            AlterColumn("dbo.MyEntity", "MyOtherEntityId", c => c.Int(nullable: false));
            CreateIndex("dbo.MyEntity", "MyOtherEntityId");
            AddForeignKey("dbo.MyEntity", "MyOtherEntityId", "dbo.MyOtherEntity", "Id");
        }
        public override void Down()
        {
            DropForeignKey("dbo.MyEntity", "MyOtherEntityId", "dbo.MyOtherEntity");
            DropIndex("dbo.MyEntity", new[] { "MyOtherEntityId" });
            AlterColumn("dbo.MyEntity", "MyOtherEntityId", c => c.Int());
        }
    }
    
  • Обновить базу данных

    > update-database
    

Другие примечания

  • Этот метод работает для миграций, применяемых во время запуска приложения.
  • Добавление более сложных сопоставлений для нового столбца в SQL возможно, но не показано здесь.

Ответ 2

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

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

public partial class LineItemProductionHistory_v4 : DbMigration
{
    public override void Up()
    {
        AddColumn("LineItemProductionHistories","TempLineItemId",c=>c.Int());
        Sql("Update LineItemProductionHistories Set TempLineItemId = LineItem_Id");
       .
       . Generated code
       .
        Sql("Update LineItemProductionHistories Set LineItemId = TempLineItemId");
        DropColumn("LineItemProductionHistories", "TempLineItemId");
    }
}

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

Ответ 3

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

Ответ 4

Быстрый ответ: во-первых, добавьте столбец с нулевым значением для ProductId, затем установите значение по умолчанию во всех существующих строках, затем установите столбец не равным нулю (если это необходимо) для будущих вставок, добавьте, наконец, добавьте ограничение внешнего ключа,

Я написал длинную запись в блоге только с полным исходным кодом - http://nodogmablog.bryanhogan.net/2015/04/entity-framework-non-null-foreign-key-migration/

Ответ 5

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

  • Запустить Add-Migration с изменениями сущностей (добавлено новое свойство ссылочной ссылки, не допускающее null, ProductId в этом случае), чтобы поднять новый класс миграции
  • Изменить новую добавленную миграцию для создания столбца с нулевым значением (nullable: true) вместо не-nullable
  • Ниже AddColumn добавьте команду Sql, задав значение столбца при необходимости
  • Ниже добавлена ​​команда AlterColumn, которая сделает столбец не нулевым как предполагалось

В приведенном выше примере это будет выглядеть так:

    public override void Up()
    {
        AddColumn("dbo.ProductFeatures", "ProductId", c => c.Int(nullable: true));
        Sql("UPDATE [dbo].[ProductFeatures] SET ProductId = (SELECT TOP 1 [Id] FROM [dbo].[Products])");
        AlterColumn("dbo.ProductFeatures", "ProductId", c => c.Int(nullable: false));
        CreateIndex("dbo.ProductFeatures", "ProductId");
        AddForeignKey("dbo.ProductFeatures", "ProductId", "dbo.Products", "Id");
    }