Рекурсия - это правильный подход, но я собираюсь предложить подход LINQ - тем более, что этот вопрос был помечен с помощью LINQ.
Одна из приятных вещей с LINQ заключается в том, что методы расширения могут убратьмного сложностей и оставляют простые запросы, которые просто выражают бизнес-логику.
Итак, вот что я использую для сглаживания рекурсивных структур:
public static IEnumerable<T> Flatten<T>(this Func<T, IEnumerable<T>> @this, T root)
{
var head = new [] { root, };
var tail =
from c in @this(root)
where !c.Equals(root)
from d in @this.Flatten(c)
select d;
return head.Concat(tail);
}
Обратите внимание на рекурсивный вызов Flatten
, и этоэто метод расширения, определенный для функции, которая возвращает дочерние элементы от данного родительского элемента.
Теперь мы можем определить Func<T, IEnumerable<T>>
следующим образом:
Func<Unit, IEnumerable<Unit>> f = u => u.Children;
И затем, предполагая, чтовсе свойства Dimension
, Parent
и Children
не равны NULL, мы можем использовать этот запрос, чтобы создать список записей для добавления в таблицу:
var records =
from r in dimensions
from d in f.Flatten(r)
select new
{
Dimension = d.Dimension.Name,
Parent = d.Parent.Name,
d.Name,
};
Теперь, если любой изсвойства null
, вот исправление.
Переопределите f
как:
Func<Unit, IEnumerable<Unit>> f = u => u.Children ?? new List<Unit>();
И добавьте этот метод расширения:
public static R ValueOrNull<T, R>(this T @this, Func<T, R> selector)
where R : class
{
return @this != null ? selector(@this) : null;
}
Теперь запрос работаеткак это:
var records =
from r in dimensions
from d in f.Flatten(r)
select new
{
Dimension = d.Dimension.ValueOrNull(x => x.Name),
Parent = d.Parent.ValueOrNull(x => x.Name),
d.Name,
};
Все еще веОчень похоже, но null
безопасно.
Надеюсь, это поможет.