Удалить каскад при удалении из одного пути - PullRequest
0 голосов
/ 20 февраля 2019

Follwing мои модели данных.Я удалил все неважные аннотации, чтобы они были короткими и чистыми.

public class Bubble
{
    public int Id { get; set; }
    public string Name { get; set; }
    public ICollection<Level> Levels { get; set; }
}

public class Level
{
    public int Id { get; set; }
    public int BubbleId { get; set; }
    [ForeignKey("ParentLevel")]
    public int? LevelId { get; set; }

    public string Name { get; set; }

    public Level ParentLevel { get; set; }
    public Bubble Bubble { get; set; }

    public ICollection<Level> Levels { get; set; }
    public ICollection<Item> Items { get; set; }
}

public class Item
{
    public int Id { get; set; }
    public int LevelId { get; set; }

    public string Name { get; set; }

    public Level Level { get; set; }
}

Если я создам его таким образом, я получу ошибку:

'Введение ограничения FOREIGN KEY' FK_dbo.Item_dbo.Level_LevelId 'в таблице' Item 'может вызывать циклы или несколько каскадных путей.Укажите ON DELETE NO ACTION или ON UPDATE NO ACTION, либо измените другие ограничения FOREIGN KEY.Не удалось создать ограничение или индекс.См. Предыдущие ошибки. '

Если я добавлю этот код, создание базы данных будет работать:

modelBuilder.Entity<Item>()
    .HasRequired(i => i.Level)
    .WithMany(l => l.Items)
    .HasForeignKey(i => i.LevelId)
    .WillCascadeOnDelete(false);

Но тогда я получу эту ошибку, когда удаляю пузырь:

SqlException: инструкция DELETE конфликтует с ограничением REFERENCE "FK_dbo.Item_dbo.Level_LevelId".Конфликт произошел в базе данных «MvBubbles1», таблице «dbo.Item», столбце «LevelId».Утверждение было прекращено.

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

Удалить код:

db.Bubbles.Remove(bubble);
db.SaveChanges();

1 Ответ

0 голосов
/ 20 февраля 2019

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

Кроме того, предположим, что вы хотите удалить пузырь2. Пузырь 2 имеет верхний уровень 20.
Пузырь 3 имеет уровень 21, который является подуровнем уровня 20.
Пузырь 4 имеет уровень 22, который является подуровнем уровня 21.

Если вы удалите Bubble 2, следует ли удалить Уровни Bubbles 3 и 4?

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

Вы можете аннулировать все LevelIds всех уровней пузыря, которые хотите удалить, перед удалением уровней и пузырей:

// we want to remove Bubble 2
var levelsToRemove = dbContext.Levels.Where(level => level.BubbleId == 2).ToList();
// nullify all levelIds:
foreach (var levelToRemove in levelsToRemove)
{
    levelToRemove.LevelId = null;
}
// TODO: maybe we need an extra SaveChanges

// Remove the Levels and the Bubble:
dbContext.Levels.RemoveRange(levelsToRemove);

var bubbleToRemove = dbContext.Find(2); // TODO: exception if not found
dbContext.Bubbles.Remove(bubbleToRemove);
dbContext.SaveChanges();

Правильное решение

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

Подумайте о том, чтобы дать свою коллекцию уровней.Это может быть отношение «один к нулю или один», или если у всех уровней Bubble 2 есть несколько общих черт, рассмотрите возможность размещения этих вещей в LevelCollection и пусть у вашего пузыря будет ноль или более LevelCollections (один ко многим).).

Каждая коллекция уровней принадлежит ровно одному пузырю.У LevelCollection есть ноль или более уровней.

Теперь гарантируется, что если LevelCollection 42 принадлежит Bubble 2, что все уровни LevelCollection 42 принадлежат Bubble 2. Вы можете удалить внешний ключ BubbleId из Level.

This выиграно 't предотвращает циклические ссылки на уровни, но это предотвратит принадлежность уровней из одного дерева к разным пузырькам

Почему каскадирование пришлось отключить

Обычно, если у вас есть отношение один ко многим, напримерШкола с его многочисленными учениками. При удалении школы вы также хотите автоматически удалить всех своих учеников.Когда включено каскадирование, тогда структура сущности сначала удалит все элементы с внешним ключом к элементу, который вы хотите переместить, прежде чем удалит ваш элемент.

Это невозможно сделать автоматически с Bubbles and Levels

Давайте добавим несколько пузырьков и уровней

                            Id | Name
Add Bubble with name A  =>  1  |  A

                                                  Id | BubbleId | LevelId
Add Level without Parent for Bubble 1         =>  10 |    1     |  null
Add sub Level of Level 10 Parent for Bubble 2 =>  11 |    1     |   10

Now give Level 10 a new LevelId               =>  10 |    1     |   11

Хорошо, каскадное включение включено, давайте удалим пузырь 1.

Прежде чем объектная структура сможет это сделать, она должна удалить все, что имеет чужойключ к Bubble 1. Итак, сначала нам нужно удалить уровень 10, чего мы не можем, потому что сначала нужно удалить уровень 11.Однако уровень 11 не может быть удален, поскольку уровень 10 не может быть удален и т. Д.

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

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

Еще одна проблема: предположим, что Bubble 2 имеет уровень 20. Уровень 20 является подчиненнымУровень уровня 21, а уровень 21 имеет внешний ключ для Bubble 1. Если вы удалите Bubble 1, что должно произойти с уровнем Bubble 2?Некоторые люди могут сказать: это становится верхним уровнем Bubble 2, другие могут сказать: нет, Bubble 2 теряет свой уровень.Entity Framework не может определить, что вы хотите, поэтому вам придется сделать это самостоятельно и отключить каскадирование.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...