开发者

LINQ expression for climbing object hierarchy?

I've got some code that needs to scan through a hierarchical database schema, looking for properties defined in the schema that have certain interesting properties. I want to make a (flat) list of the names of these properties.

To (hopefully) reduce confusion, I'll call the classes that are defined in the schema "Kinds", because the schema does not describe a C# class hierarchy.

The schema is available to me as a tree of .NET objects; I don't need to parse any XML or anything. The issue is that I'll be entering the schema tree at various points, and I need to make sure that I'm aware of interesting properties that are being inherited from base kinds as well as those that are directly defined on the kind that I'm currently looking at.

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

Anyway, I'm wondering if it's possible to w开发者_如何转开发rite an equivalent LINQ statement. The outermost foreach loops are trivial (there's actually another one that I've left out for clarity) but I'm not sure whether it's possible to capture the iteration up the hierarchy in a LINQ query.

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

I guess this is similar to writing a LINQ query that starts with a node in a linked list and keeps iterating through the linked list until it gets to the end. Is that possible?


You can't really do this with 'pure' LINQ, but you can mix this with an enumerator method, and you'd be on the right track. For instance, define an extension method on the Kind type like this:

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

Now you can use this method in your LINQ query:

from kind in objDescription.PossibleKinds
from baseKind in kind.GetInstanceAndBaseKinds()
from property in baseKind.PropertyDefinitions
where property.IsInteresting
select property;


Here is a post with introduces an extension method Descendants() which might led you in the right direction.


You could make a recursive 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);
}

Notice how you need to separate declaration from assignment of the action though. Hope that helps.


pls consider this suggestion:

Used LinqToSQL to map a table 'dbo.Groups', generating following class 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); }
    }    
}

Then added following LinqExtension (courtesy of 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;
            }
        }
    }
}

at last, added following methods to my 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));
    }
}

If I understood your question, the Genealogy method may help you.


Although Steven's answer is probably better, the implementation I use is an Ancestors property on the object:

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

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜