По иронии судьбы, самой сложной частью этой проблемы может быть получение ограниченного набора записей из базы данных, которые удовлетворяют определенному предикату пути. Если вы не используете базу данных, обеспечивающую поддержку иерархических запросов (например, Oracle CONNECT BY), это может стать довольно сложным. Проверьте этот ТАК вопрос для некоторых подходов с этой стороны.
Но давайте предположим, что вам удалось загрузить свои данные... вы можете сделать это либо в пользовательскую коллекцию объектов, либо в набор данных, либо в структуру XML (если вам не нужно манипулировать данными, кроме как струны). Если у вас есть настраиваемый объект, вы можете добавить свойство, которое предоставляет дочернюю коллекцию, запросив весь набор данных на наличие записей, совпадающих с родительским ключом (см. пример ниже). Если количество записей велико, это становится неэффективным из-за затрат на поиск в коллекции. Именно здесь решение DataSet сияет, так как вы можете добавить пользовательские индексы в столбец ParentID, которые повысят производительность поиска. Вы можете прочитать о типизированных наборах данных здесь, чтобы узнать больше о том, как расширить пример ниже в этом направлении.
Вот пример пользовательского объекта с поддержкой навигации по дереву. Я использую структуру каталогов (папки), потому что это естественный пример иерархических данных. Опять же, будьте осторожны при использовании этого кода без индексированной структуры, так как полный дочерний обход будет O(n2).
class Folder // could be converted to a DataRow-derivative
{
public int Id { get; set; }
public string Name { get; set; }
public int? ParentId { get; set; }
public IEnumerable<Folder> GetChildren( IEnumerable<Folder> allFolders )
{
// NOTE: becomes expensive on large hierarchies on unindexed data
// a slightly better version, could replace IEnumerable<Folder>
// with IOrderedEnumerable<Folder> which would allow the use of
// a binary search algorithm for finding children (assuming it's
// ordered on the ParentId property).
return AllFolders.Where( x => x.ParentId.HasValue &&
x.ParentId.Value = this.Id );
}
}
// elsewhere in your code...
void LoadAndPrintFolders()
{
// load the folder data... from database, or wherever
IEnumerable<Folder> allFolders = LoadFolders();
// find all root folders (let's say, those that have a null ParentId
IEnumerable<Folder> rootFolders =
allFolders.Where( x => !x.ParentId.HasValue );
// iterate over all of the data as a tree structure...
foreach( var folder in rootFolders )
{
PrintFolder( allFolders, folder, 0 );
}
}
// recursive function to print all folders...
void PrintFolder( IEnumerable<Folder> allFolders, Folder f, int level )
{
Console.WriteLine( "{0}{1}", new string('\t',level), f.Name );
foreach( var folder in f.GetChildren( allFolders )
{
PrintFolder( allFolders, folder, level + 1 );
}
}
person
LBushkin
schedule
25.06.2009