Подтвердить что ты не робот

Как удалить дочерние сущности перед родителем с помощью Entity Framework CF?

Я пытаюсь сначала использовать EF-код, чтобы удалить запись db (deleteMe), а потом дети (deleteMe.Prices).

foreach (var deleteMe in deleteThese)
{ 
   // Delete validation
   if(CanDeleteItem(deleteMe.ItemId))
   {
      db.Entry(deleteMe).State = EntityState.Deleted;

      foreach (var item in deleteMe.Prices)
      {
         db.Entry(item).State = EntityState.Deleted; // cascade delete
      }
   }
}
db.SaveChanges();

Тем не менее, Entity Framework, похоже, не может отслеживать тот факт, что дочерние записи должны быть удалены перед родителем. Я получаю сообщение об ошибке:

Оператор DELETE противоречил ограничению REFERENCE "ItemPrice_Item".
Конфликт произошел в базе данных "DEVDB", таблице "dbo.ItemPrices", column 'Item_ItemId'.
Заявление завершено.

Как выполнить это удаление в EF?

4b9b3361

Ответ 1

Я закончил поиск быстрой строки, которая сделает это для меня:

foreach (var deleteMe in deleteThese)
{ 
   // Delete validation
   if(CanDeleteItem(deleteMe.ItemId))
   {
      ///
      deleteMe.Prices.ToList().ForEach(p => db.ItemPrices.Remove(p));
      ///

      db.Entry(deleteMe).State = EntityState.Deleted;
   }
}
db.SaveChanges();

Ответ 2

EF6

context.Children.RemoveRange(parent.Children)

Ответ 3

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

Ответ 4

Наилучшим образом самым простым решением было бы сначала перебрать цены и вызвать сохранение изменений, а затем установить запись для удаления для deleteMe и снова вызвать изменения сохранения, но вы проверили это: Код структуры Entity сначала удаляется с помощью каскада? Кажется, вы хотите.

Любопытно, но почему вы просто не удаляете сущности из контекста для удаления, а вместо этого устанавливаете состояние записи?

Другой вариант - установить каскадное удаление http://blogs.msdn.com/b/alexj/archive/2009/08/19/tip-33-how-cascade-delete-really-works-in-ef.aspx

Сделайте что-нибудь подобное (не проверено, но, надеюсь, вы получите jist):

using (TransactionScope scope = new TransactionScope())
{    
    foreach (var deleteMe in deleteThese)
    { 
   // Delete validation
      if(CanDeleteItem(deleteMe.ItemId))
      {

         foreach (var item in deleteMe.Prices)
         {
            db.Entry(item).State = EntityState.Deleted; // cascade delete
         }
         db.SaveChanges();

         db.Entry(deleteMe).State = EntityState.Deleted;


     }
   }
   db.SaveChanges();
   scope.Complete();
}     

Дополнительно вы можете позвонить:

db.Prices.Remove(item);

и

db.DeleteMes.Remove(deleteMe);

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

Ответ 5

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

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

Ответ 6

Если ваш объект является саморегуляцией, вы можете удалить как "много-ко-многим", так и "один ко многим", используя приведенный ниже метод. Просто не забудьте затем вызвать db.SaveChanges():)

[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public ActionResult DeleteConfirmed(int id)
{
    Object obj = this.db.Objects.Find(id);
    this.DeleteObjectAndChildren(obj);
    this.db.Objects.Remove(obj);
    this.db.SaveChanges();
    return this.Json(new { success = true });
}

/// <summary>
/// This deletes an object and all children, but does not commit changes to the db.
///  - MH @ 2016/08/15 14:42
/// </summary>
/// <param name="parent">
/// The object.
/// </param>
private void DeleteObjectAndChildren(Object parent)
{
    // Deletes One-to-Many Children
    if (parent.Things != null && parent.Things.Count > 0)
    {
        this.db.Things.RemoveRange(parent.Things);
    }

    // Deletes Self Referenced Children
    if (parent.Children != null && parent.Children.Count > 0)
    {
        foreach (var child in parent.Children)
        {
            this.DeleteObjectAndChildren(child);
        }

        this.db.Objects.RemoveRange(parent.Children);
    }
}

Ответ 7

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

Мое исправление заключалось в добавлении атрибутов, указанных ниже, в класс Child для свойства, представляющего его Parent Id

    public class Child
    {
        [Key, Column(Order = 1)]
        public string Id { get; set; }

        [Key, ForeignKey("Parent"), Column(Order = 2)]  // adding this line fixed things for me
        public string ParentId {get; set;}
    }

    public class Parent
    {
        [Key, Column(Order = 1)]
        public string Id { get; set; }

        ...

        public virtual ICollection<Child> Children{ get; set; }
    }

Ответ 8

Следующее работает довольно эффективно. Для каждой реляционной таблицы в вашей базе данных добавьте следующее (в вашем файле контекста).

protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder
            .Entity<TableA>()
            .HasMany(x => x.TableB)
            .WithRequired(x => x.TableA)
            .WillCascadeOnDelete();

        modelBuilder
            .Entity<TableC>()
            .HasMany(x => x.TableD)
            .WithRequired(x => x.TableC)
            .WillCascadeOnDelete();

        modelBuilder
            .Entity<TableE>()
            .HasMany(x => x.TableF)
            .WithRequired(x => x.TableE)
            .WillCascadeOnDelete(); }

Затем в своем коде не забудьте загрузить эти таблицы перед удалением

            context.TableA.Load();
            context.TableB.Load();
            context.TableC.Load();
            context.TableD.Load();
            context.TableE.Load();
            context.TableF.Load();

            var tableAEntity= TableA.Where(x => x.Condition == [yourcondition].FirstOrDefault(); 

            context.TableA.Remove(tableAEntity);
            context.SaveChanges();

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