#c#
#c#
Вопрос:
У меня есть объект таблицы базы данных, который:
[Table("TreeViewDb")]
public class TreeViewDb
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public int ParentId { get; set; }
public string Name { get; set; }
}
И у меня есть модель представления, которая :
public class TreeView
{
public TreeView()
{
Children = new List<TreeView>();
}
public int Id { get; set; }
public int ParentId { get; set; }
public string Name { get; set; }
// children
public List<TreeView> Children { get; set; }
}
Теперь мне нужно сохранить TreeView в базе данных. Во время сохранения детей или детей младше детей до n-го уровня. Но мой приведенный ниже метод подходит только для уровня 3. Как я могу перейти на n-й уровень, чтобы сохранить дочерние и родительские объекты рекурсивным способом?
public bool SaveOrUpdateTreeView(TreeView viewModel)
{
// Level 1
var parentModel = new TreeViewDb
{
Id = viewModel.Id,
ParentId = viewModel.ParentId,
Name = viewModel.Name
};
// Save or update object and return primary key
var parentId = _dataRepository.SaveOrUpdateTreeView(parentModel);
// Level 2
foreach (var child in viewModel.Children)
{
var childModel = new TreeViewDb
{
Id = viewModel.Id,
ParentId = parentId, // Parent Primary Key
Name = viewModel.Name
};
// Save or update object and return primary key
var childId = _dataRepository.SaveOrUpdateTreeView(childModel);
// Level 3
foreach (var grandChild in child.Children)
{
var grandChildModel = new TreeViewDb
{
Id = viewModel.Id,
ParentId = childId, // Child Primary Key
Name = viewModel.Name
};
_dataRepository.SaveOrUpdateTreeView(grandChildModel);
}
}
return true;
}
Ответ №1:
Вот ты где:
- используйте
Stack
, включая глубину элемента - перебирайте узлы по порядку
- решайте сохранять его или нет
Код:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace zzzzzzzz
{
internal static class Program
{
private static void Main(string[] args)
{
var node = new Node("node 0 @ level 0")
{
new Node("node 1 @ level 1")
{
new Node("node 2 @ level 2")
{
new Node("node 3 @ level 3")
{
new Node("node 4 @ level 4")
}
}
},
new Node("node 5 @ level 1")
{
new Node("node 6 @ level 2")
{
new Node("node 7 @ level 3")
}
}
};
var stack = new Stack<(Node, int)>();
stack.Push((node, 0));
while (stack.Any())
{
var (current, depth) = stack.Pop();
ProcessNode(current, depth);
foreach (var item in current.Reverse())
{
stack.Push((item, depth 1));
}
}
}
private static void ProcessNode(Node node, int depth)
{
Console.Write($"{new string('t', depth)}{node}");
var color = Console.ForegroundColor;
Console.ForegroundColor = depth < 3 ? ConsoleColor.Green : ConsoleColor.Red;
Console.Write($" {(depth < 3 ? "SAVED" : "IGNORED")}{Environment.NewLine}");
Console.ForegroundColor = color;
}
}
public class Node : IList<Node>
{
public Node(string text)
{
Text = text;
}
public string Text { get; set; }
private IList<Node> List { get; } = new List<Node>();
public IEnumerator<Node> GetEnumerator()
{
return List.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable) List).GetEnumerator();
}
public void Add(Node item)
{
List.Add(item);
}
public void Clear()
{
List.Clear();
}
public bool Contains(Node item)
{
return List.Contains(item);
}
public void CopyTo(Node[] array, int arrayIndex)
{
List.CopyTo(array, arrayIndex);
}
public bool Remove(Node item)
{
return List.Remove(item);
}
public int Count => List.Count;
public bool IsReadOnly => List.IsReadOnly;
public int IndexOf(Node item)
{
return List.IndexOf(item);
}
public void Insert(int index, Node item)
{
List.Insert(index, item);
}
public void RemoveAt(int index)
{
List.RemoveAt(index);
}
public Node this[int index]
{
get => List[index];
set => List[index] = value;
}
public override string ToString()
{
return $"{nameof(Text)}: {Text}";
}
}
}
Комментарии:
1. В настоящее время мой метод поддерживает сохранение трехуровневых данных. Но я хочу реорганизовать свою функцию для поддержки сохранения n-уровня данных. Так что мне не нужна глубина.
Ответ №2:
Вы можете создать универсальный итератор, который позволит вам перебирать все узлы в дереве:
public static IEnumerable<(T Parent, T Node, int Level)> BreadthFirst<T>(T self, Func<T, IEnumerable<T>> selector)
{
var queue = new Queue<(T Parent, T Node, int Level)>();
queue.Enqueue((self, self, 0));
while (queue.Count > 0)
{
var current = queue.Dequeue();
yield return current;
var node = current.Node;
var level = current.Level 1;
foreach (var child in selector(node))
{
queue.Enqueue((node, child, level));
}
}
}
Вызывается как
var nodes = BreadthFirst(viewModel, v => v.Children).Where(t => t.Level < 3);
Корневой узел будет иметь самого себя в качестве родительского, поэтому вам нужно будет проверить это, если вы хотите обработать его каким-то другим способом.