1

У меня возникли проблемы, когда вы играете с иерархическими данными и пытаетесь удалить узел с детьми.MVC 5 Entity Framework 6 - удаление вложенных/иерархических данных

Я создал образец дерева, похожий на this article, и украсил элементы ul-li немного загрузочным (добавьте, удалите и отредактируйте значки).

Я сейчас пытаюсь найти рекурсивное удаление, так как я использую IEnumerable (Children), который является обратным результатом parentNodeID. Поэтому при удалении узла дети были обновлены, и система выбрасывает ошибку перечисления (коллекция изменилась). У меня была такая ошибка несколько лет назад, но я не помню, как я ее исправил. Любой совет?

Так вот что я получил:

~/Модели/Tree.cs

public class Tree 
{ 
    [Key] 
    public int NodeID { get; set; } 

    [Display(Name ="Element name")] 
    public string NodeName { get; set; } 

    [Display(Name ="Element identifier")] 
    public string NodeIdentifier { get; set; } 

    public int? ParentNodeID { get; set; } 
    public virtual Tree Parent { get; set; } 
    public virtual ICollection<Tree> Children { get; set; } 
} 

~/Контроллеры/TreesController.cs

// Partial - only Delete Actions 

// GET: Trees/Delete/5 
public async Task<ActionResult> Delete(int? id) 
{ 
    if (id == null) 
    { 
     return new HttpStatusCodeResult(HttpStatusCode.BadRequest); 
    } 
    Tree tree = await db.Trees.FindAsync(id); 
    if (tree == null) 
    { 
     return HttpNotFound(); 
    } 

    if (tree.Children.Count > 0) 
    { 
     ViewBag.HasChildren = true; 
    } 
    else 
    { 
     ViewBag.HasChildren = false; 
    } 

    return View(tree); 
} 

// POST: Trees/Delete/5 
[HttpPost, ActionName("Delete")] 
[ValidateAntiForgeryToken] 
public async Task<ActionResult> DeleteConfirmed(int id) 
{ 
    Tree tree = await db.Trees.FindAsync(id); 
    DeleteRecursive(tree); 
    db.Trees.Remove(tree); 
    await db.SaveChangesAsync(); 
    return RedirectToAction("Index"); 
} 

public void DeleteRecursive(Tree tree) 
{ 
    foreach(var child in tree.Children) 
    { 
     DeleteRecursive(child); 
    } 
    db.Trees.Remove(tree); 
} 

~/Просмотры/Деревья/Index.cshtml

<div class="container"> 
@helper BuildTree(IEnumerable<MVCMusicStore.Models.Tree> tree, int? parentID = null) 
{ 
    var nodes = tree.Where(t => t.ParentNodeID == parentID).OrderBy(n => n.NodeIndexOrder); 
    if (nodes.Any()) 
    { 
     if (nodes.First().ParentNodeID == null) 
     { 
       <ul id="tree"> 
        @foreach (var node in nodes) 
        { 
         <li> 
          @node.NodeIndexOrder - @node.NodeName &nbsp; <a href="@Url.Action("Edit","Trees",new { id = node.NodeID })" data-toggle="tooltip" data-placement="right" title="Bearbeiten"><i class="glyphicon glyphicon-edit"></i></a>&nbsp; <a href="@Url.Action("Delete","Trees",new { id = node.NodeID })" data-toggle="tooltip" data-placement="right" title="Löschen"><i class="glyphicon glyphicon-trash"></i></a> 
          @BuildTree(tree, node.NodeID) 
         </li> 
        } 
       </ul> 
     } 
     else 
     { 
       <ul> 
        @foreach (var node in nodes) 
        { 
         <li> 
          @node.NodeIndexOrder - @node.NodeName &nbsp; <a href="@Url.Action("Edit","Trees",new { id = node.NodeID })" data-toggle="tooltip" data-placement="right" title="Bearbeiten"><i class="glyphicon glyphicon-edit"></i></a>&nbsp; <a href="@Url.Action("Delete","Trees",new { id = node.NodeID })" data-toggle="tooltip" data-placement="right" title="Löschen"><i class="glyphicon glyphicon-trash"></i></a> 
          @BuildTree(tree, node.NodeID) 
         </li> 
        } 
       </ul> 
     }   
    } 
    else 
    { 

     <p> 
      <a href="@Url.Action("Create","Trees", new { parentNodeID = parentID })" data-toggle="tooltip" data-placement="right" title="Neu"><i class="glyphicon glyphicon-plus"></i></a> 
     </p> 
    } 
} 

@BuildTree(Model,null) 
</div> 

Edit - Решение:

public void DeleteRecursive(Tree tree) 
{ 
    foreach(var child in tree.Children.ToArray<Tree>()) 
    { 
     DeleteRecursive(child); 
    } 
    db.Trees.Remove(tree); 
} 

Использование массива для удаления из не изменяет оригинальную коллекцию.

Credits to Johnathon Sullinger: Delete item in nested collections of Nth level

+0

Коллекция изменилась? Попробуйте использовать цикл 'for' вместо цикла' foreach' в методе 'DeleteRecursive'. –

+0

А, у меня есть лучшее решение, чем использование for-loop (что своего рода old-skool imho). – SnakeByte

+0

Что такое старая школа о 'for'? Теперь вы создаете массив, который вам не нужен. В любом случае, если вы решите свою собственную проблему, все в порядке, чтобы написать ответ на свой вопрос и даже пометить его как принятое в свое время. –

ответ

0
public void DeleteRecursive(Tree tree) 
{ 
    foreach(var child in tree.Children.ToArray<Tree>()) 
    { 
     DeleteRecursive(child); 
    } 
    db.Trees.Remove(tree); 
} 

Использование массива для удаления из не изменяет оригинальную коллекцию.