Представление ограничения FOREIGN KEY может вызвать циклы или несколько каскадных путей - почему?

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

Представление ограничения FOREIGN KEY 'FK_dbo.Sides_dbo.Cards_CardId' on Таблица "Стороны" может вызывать циклы или несколько каскадных путей. Укажите ON УДАЛИТЬ НЕТ ДЕЙСТВИЯ или НЕ ОБНОВИТЬ НЕТ ДЕЙСТВИЙ, или изменить другой ИНОСТРАННЫЙ КЛЮЧ ограничения.

Здесь моя Карточка:

public class Card
{
    public Card()
    {
        Sides = new Collection<Side>();
        Stage = Stage.ONE;
    }

    [Key]
    [Required]
    public virtual int CardId { get; set; }

    [Required]
    public virtual Stage Stage { get; set; }

    [Required]
    [ForeignKey("CardId")]
    public virtual ICollection<Side> Sides { get; set; }
}

Здесь мой объект Сторона:

public class Side
{
    public Side()
    {
        Stage = Stage.ONE;
    }

    [Key]
    [Required]     
    public virtual int SideId { get; set; } 

    [Required]
    public virtual Stage Stage { get; set; }

    [Required]
    public int CardId { get; set; }

    [ForeignKey("CardId")]
    public virtual Card Card { get; set; }

}

И здесь мой объект Stage:

public class Stage
{
    // Zero
    public static readonly Stage ONE = new Stage(new TimeSpan(0, 0, 0), "ONE");
    // Ten seconds
    public static readonly Stage TWO = new Stage(new TimeSpan(0, 0, 10), "TWO");

    public static IEnumerable<Stage> Values
    {
        get
        {
            yield return ONE;
            yield return TWO;
        }

    }

    public int StageId { get; set; }
    private readonly TimeSpan span;
    public string Title { get; set; }

    Stage(TimeSpan span, string title)
    {
        this.span = span;
        this.Title = title;
    }

    public TimeSpan Span { get { return span; } }
}

Что странно, если я добавлю следующее в мой класс Stage:

    public int? SideId { get; set; }
    [ForeignKey("SideId")]
    public virtual Side Side { get; set; }

Миграция выполняется успешно. Если я открою SSMS и посмотрю на таблицы, я вижу, что Stage_StageId был добавлен в Cards (как ожидалось/желательно), однако Sides не содержит ссылки на Stage (не ожидается).

Если я затем добавлю

    [Required]
    [ForeignKey("StageId")]
    public virtual Stage Stage { get; set; }
    public int StageId { get; set; }

В класс Side, я вижу столбец StageId, добавленный в мою таблицу Side.

Это работает, но теперь во всем приложении любая ссылка на Stage содержит SideId, которая в некоторых случаях совершенно не имеет значения. Я хотел бы просто предоставить объектам Card и Side свойство Stage, основанное на вышеуказанном классе Stage, без загрязнения класса сцены ссылочными свойствами, если возможно... что я делать не так?

Ответ 1

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

  • удаление будет каскадно направлено на Side
  • удаление будет каскадно идти непосредственно к Card, а поскольку у Card и Side есть обязательное отношение "один ко многим", и по умолчанию снова включено каскадное удаление, оно затем будет каскадно переходить с Card на Side

Итак, у вас есть два каскадных пути удаления от Stage до Side - что вызывает исключение.

Вы должны либо сделать Stage необязательным, по крайней мере, в одной из сущностей (т.е. удалить атрибут [Required] из свойств Stage), либо отключить каскадное удаление с помощью Fluent API (невозможно с аннотациями данных):

modelBuilder.Entity<Card>()
    .HasRequired(c => c.Stage)
    .WithMany()
    .WillCascadeOnDelete(false);

modelBuilder.Entity<Side>()
    .HasRequired(s => s.Stage)
    .WithMany()
    .WillCascadeOnDelete(false);

Ответ 2

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

[ForeignKey("StageId")]
public virtual Stage Stage { get; set; }
public int? StageId { get; set; }

Ответ 3

Кто-нибудь задается вопросом, как это сделать в ядре EF:

      protected override void OnModelCreating(ModelBuilder modelBuilder)
            {
                foreach (var relationship in modelBuilder.Model.GetEntityTypes().SelectMany(e => e.GetForeignKeys()))
                {
                    relationship.DeleteBehavior = DeleteBehavior.Restrict;
                }
           ..... rest of the code.....

Ответ 4

Я получал эту ошибку для большого количества объектов, когда я переносился из модели EF7 в версию EF6. Я не хотел проходить через каждую сущность по одному, поэтому я использовал:

builder.Conventions.Remove<ManyToManyCascadeDeleteConvention>();
builder.Conventions.Remove<OneToManyCascadeDeleteConvention>();

Ответ 5

Вы можете установить cascadeDelete в false или true (в методе миграции Up()). Зависит от вашего требования.

AddForeignKey("dbo.Stories", "StatusId", "dbo.Status", "StatusID", cascadeDelete: false);

Ответ 6

У меня тоже была эта проблема, я сразу решил ее с помощью этого ответа из аналогичного потока

В моем случае я не хотел удалять зависимую запись при удалении ключа. Если это имеет место в вашей ситуации, просто просто измените значение Boolean при переходе на false:

AddForeignKey("dbo.Stories", "StatusId", "dbo.Status", "StatusID", cascadeDelete: false);

Скорее всего, если вы создаете отношения, которые вызывают эту ошибку компилятора, но DO хотят сохранить каскадное удаление; у вас есть проблема с вашими отношениями.

Ответ 7

В .NET Core я изменил параметр onDelete на ReferencialAction.NoAction

         constraints: table =>
            {
                table.PrimaryKey("PK_Schedule", x => x.Id);
                table.ForeignKey(
                    name: "FK_Schedule_Teams_HomeId",
                    column: x => x.HomeId,
                    principalTable: "Teams",
                    principalColumn: "Id",
                    onDelete: ReferentialAction.NoAction);
                table.ForeignKey(
                    name: "FK_Schedule_Teams_VisitorId",
                    column: x => x.VisitorId,
                    principalTable: "Teams",
                    principalColumn: "Id",
                    onDelete: ReferentialAction.NoAction);
            });

Ответ 8

Я исправил это. Когда вы добавляете миграцию, в методе Up() будет такая строка:

.ForeignKey("dbo.Members", t => t.MemberId, cascadeDelete:True)

Если вы просто удалите cascadeDelete с конца, он будет работать.

Ответ 9

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

Добавьте этот метод в класс контекстной базы данных:

protected override void OnModelCreating(DbModelBuilder modelBuilder) {
    modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
}

Ответ 10

Это звучит странно, и я не знаю почему, но в моем случае это происходило, потому что мой ConnectionString использовал ".". в атрибуте "источник данных". Как только я изменил его на "localhost", он работал как шарм. Никаких других изменений не требовалось.

Ответ 11

Существующие ответы великолепны. Я просто хотел добавить, что столкнулся с этой ошибкой по другой причине. Я хотел создать начальную миграцию EF для существующей БД, но я не использовал флаг -IgnoreChanges и применил команду Update-Database к пустой базе данных (также к существующим сбоям).

Вместо этого мне пришлось выполнить эту команду, когда текущая структура БД является текущей:

Add-Migration Initial -IgnoreChanges

Вероятно, существует реальная проблема в структуре БД, но спасти мир по одному шагу за раз...

Ответ 12

В .NET Core я играл со всеми верхними ответами - но без каких-либо успехов. Я много изменил структуру БД и каждый раз добавлял новую попытку перехода на update-database, но получал ту же ошибку.

Затем я начал remove-migration один за другим, пока консоль диспетчера пакетов не сделала мне исключение:

Миграция '20170827183131 _ ***' уже применяется к базе данных

После этого я добавил новую миграцию (add-migration) и update-database успешно

Итак, мое предложение было бы: очистить все ваши временные миграции до вашего текущего состояния БД.

Ответ 13

Ни одно из вышеупомянутых решений не помогло мне. Мне нужно было использовать пустое значение int (int?) Для внешнего ключа, который не требовался (или не являлся ненулевым ключом столбца), а затем удалить некоторые из моих миграций.

Начните с удаления миграций, затем попробуйте обнуляемый int.

Проблемой была как модификация, так и дизайн модели. Нет необходимости в изменении кода.

Ответ 14

Самый простой способ - отредактировать файл миграции (cascadeDelete: true) в (cascadeDelete: false), а затем после назначения команды Update-Database в консоли диспетчера пакетов. Если это проблема с последней миграцией, тогда все в порядке. В противном случае проверьте свою предыдущую историю миграции, скопируйте эти вещи, вставьте в свой последний файл миграции, после чего сделайте то же самое. это прекрасно работает для меня.