LINQ выражение для восхождения на иерархию объектов? - PullRequest
2 голосов
/ 16 декабря 2010

У меня есть код, который должен сканировать иерархическую схему базы данных и искать свойства, определенные в схеме, которые имеют определенные интересные свойства.Я хочу сделать (плоский) список имен этих свойств.

Чтобы (надеюсь) уменьшить путаницу, я назову классы, определенные в схеме, "Виды", поскольку схема не описывает иерархию классов C #.

Схема доступнадля меня как дерево объектов .NET;Мне не нужно разбирать любой XML или что-нибудь.Проблема в том, что я буду входить в дерево схемы в разных точках, и мне нужно убедиться, что я знаю об интересных свойствах, которые наследуются от базовых видов, а также о тех свойствах, которые прямо определены для вида, который яСейчас смотрю.

results = new List<PropertyDefinition>;

foreach (Kind objKind in objDescription.PossibleKinds)
{
    // Iterate up the schema hierarchy
    while (objKind != null) 
    {
        foreach (PropertyDefinition prop in objKind.PropertyDefinitions)
        {
            if (prop.IsInteresting)
                results.Add(prop);
        }
        // Move up a level in the hierarchical relationship
        objKind = objKind.BaseKind;
    }
}

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

Ответы [ 5 ]

2 голосов
/ 16 декабря 2010

Вы действительно не можете сделать это с «чистым» LINQ, но вы можете смешать это с помощью метода перечислителя, и вы окажетесь на правильном пути.Например, определите метод расширения для типа Kind следующим образом:

public static IEnumerable<Kind> GetInstanceAndBaseKinds(
    this Kind instance)
{
    while (instance != null)
    {
        yield return instance;
        instance = instance.BaseKind;
    }
}

Теперь вы можете использовать этот метод в своем запросе LINQ:

from kind in objDescription.PossibleKinds
from baseKind in kind.GetInstanceAndBaseKinds()
from property in baseKind.PropertyDefinitions
where property.IsInteresting
select property;
1 голос
/ 16 декабря 2010

Вот пост с введением метода расширения Descendants(), который может привести вас в правильном направлении.

0 голосов
/ 16 декабря 2010

Хотя ответ Стивена, вероятно, лучше, я использую реализацию как свойство Ancestors объекта:

partial class Kind
{
    public IEnumerable<Kind> Ancestors
    {
        get
        {
            for (var p = BaseKind; p != null; p = p.BaseKind)
                yield return p;
        }
    }

    public IEnumerable<Kind> ThisAndAncestors
    {
        get
        {
            for (var p = this; p != null; p = p.BaseKind)
                yield return p;
        }
    }
}
0 голосов
/ 16 декабря 2010

Просьба рассмотреть это предложение:

Использовал LinqToSQL для отображения таблицы 'dbo.Groups', генерируя следующий класс Group

[Table(Name = "dbo.Groups")]
public partial class Group
{
    [Column(Storage = "_Id", AutoSync = AutoSync.OnInsert, DbType = "Int NOT NULL IDENTITY", IsPrimaryKey = true, IsDbGenerated = true)]
    public int Id
    {
        get { return this._Id; }
        set { this._Id = value; }
    }

    [Column(Storage = "_ParentId", DbType = "Int")]
    public System.Nullable<int> ParentId
    {
        get { return this._ParentId; }
        set { this._ParentId = value; }
    }
    [Association(Name = "Group_Group", Storage = "_Children", ThisKey = "Id", OtherKey = "ParentId")]
    public EntitySet<Group> Children
    {
        get { return this._Children; }
        set { this._Children.Assign(value); }
    }    
}

Затем добавлено после LinqExtension (предоставлено как получить древовидную структуру данных таблицы по linq )

public static class LinqExtensions
{
    static public IEnumerable<T> Descendants<T>(this IEnumerable<T> source, Func<T, IEnumerable<T>> DescendBy)
    {
        foreach (T value in source)
        {
            yield return value;

            foreach (T child in DescendBy(value).Descendants<T>(DescendBy))
            {
                yield return child;
            }
        }
    }
}

наконец, добавил следующие методы в мой групповой класс:

public partial class Group
{

    public IEnumerable<Group> Descendants()
    {
        return LinqExtensions.Descendants(Children, c => c.Children);
    }
    public IEnumerable<Group> Genealogy()
    {
        Group[] ancestor = new Group[] { this };
        return ancestor.Concat(LinqExtensions.Descendants(Children, c => c.Children));
    }
}

Если я понял ваш вопрос, вам может помочь генеалогический метод.

0 голосов
/ 16 декабря 2010

Вы можете сделать рекурсив Action<Kind, List<PropertyDefinition>:

Action<Kind, List<PropertyDefinition> action = null;
action = (k, l) => {
  if (k == null) return;

  foreach (var definition in k.PropertyDefinitions)
  {
    if (definition.IsInteresting)
      l.Add(definition);
  }

  action(k.BaseKind, l);
};

var results = new List<PropertyDefinition>();
foreach (var kind in objDescription.PossibleKinds)
{
  action(kind, results);
}

Обратите внимание, как вам нужно отделить объявление от назначения действия. Надеюсь, это поможет.

...