攀爬对象层次结构的LINQ表达式?

时间:2010-12-16 10:57:19

标签: c# linq recursion linq-to-objects

我有一些代码需要扫描分层数据库模式,寻找在模式中定义的具有某些有趣属性的属性。我想制作这些属性名称的(平面)列表。

为了(希望)减少混淆,我将调用模式“Kinds”中定义的类,因为模式不描述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查询中捕获层次结构中的迭代。

var query = from objKind in objDescription.PossibleKinds
            // What goes here?
            from prop in objKind.PropertyDescriptions
            where prop.IsInteresting
            select prop;

我想这类似于编写LINQ查询,该查询以链表中的节点开始,并一直迭代链表直到结束。这可能吗?

5 个答案:

答案 0 :(得分:2)

你不能用'纯'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 :(得分:1)

Here is a post引入了一种扩展方法Descendants(),可能会让您朝着正确的方向前进。

答案 2 :(得分:0)

你可以做一个递归的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);
}

注意你需要如何将声明与动作的分配分开。希望有所帮助。

答案 3 :(得分:0)

请考虑这个建议:

使用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之后添加(由how-to-get-a-tree-structured-table-data-by-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;
            }
        }
    }
}

最后,将以下方法添加到我的Group Class:

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));
    }
}

如果我理解你的问题,Genealogy方法可以帮助你。

答案 4 :(得分:0)

虽然Steven的答案可能更好,但我使用的实现是对象上的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;
        }
    }
}