2010-12-16 11 views
2

Ich habe Code, der ein hierarchisches Datenbankschema durchsucht und nach Eigenschaften sucht, die im Schema definiert sind und bestimmte interessante Eigenschaften haben. Ich möchte eine (flache) Liste der Namen dieser Eigenschaften erstellen.LINQ Ausdruck für Klettern Objekthierarchie?

Um (hoffentlich) Verwirrung zu vermeiden, werde ich die Klassen aufrufen, die im Schema "Kinds" definiert sind, weil das Schema keine C# -Klassenhierarchie beschreibt.

Das Schema ist für mich als eine Struktur der .NET-Objekte verfügbar; Ich muss kein XML oder irgendetwas analysieren. Das Problem ist, dass ich den Schemabaum an verschiedenen Punkten betrete und ich muss sicherstellen, dass mir interessante Eigenschaften bekannt sind, die von Basistypen geerbt werden, ebenso wie solche, die direkt auf die Art, die ich habe, definiert sind schaue mir gerade an.

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

Wie auch immer, ich frage mich, ob es möglich ist, eine äquivalente LINQ-Anweisung zu schreiben. Die äußersten foreach Schleifen sind trivial (es gibt tatsächlich eine andere, die ich aus Gründen der Klarheit weggelassen habe), aber ich bin nicht sicher, ob es möglich ist, die Iteration in der Hierarchie in einer LINQ-Abfrage zu erfassen.

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

Ich denke, das ist ähnlich wie eine LINQ-Abfrage zu schreiben, die in einer verknüpften Liste mit einem Knoten beginnt und halten durch die verkettete Liste iterieren, bis es zu Ende geht. Ist das möglich?

Antwort

2

Sie können dies nicht wirklich mit 'reinem' LINQ tun, aber Sie können dies mit einer Enumerator-Methode mischen, und Sie wären auf dem richtigen Weg. Zum Beispiel, eine Erweiterungsmethode auf dem Kind Typ wie folgt definieren:

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

Jetzt können Sie diese Methode in der LINQ-Abfrage verwenden:

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

es Schlag mich. Ich hatte fast die gleiche Antwort. Ich habe nur eine Erweiterungsmethode anstelle einer in der 'Kind' Klassendefinition verwendet. Sonst identisch. Du hast meine Aufwertung. – Enigmativity

+0

In diesem Fall habe ich keine Kontrolle über die Kind-Klasse-Definition, so dass es aussieht, als wäre die Erweiterungsmethode auch nützlich. Danke euch beiden! – Hydrargyrum

1

Here is a post mit führt eine Erweiterungsmethode Descendants(), die Sie in die richtige Richtung geführt haben könnte.

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

Beachten Sie, wie Sie obwohl Erklärung Zuordnung der Aktion trennen müssen:

0

Sie könnten eine rekursive Action<Kind, List<PropertyDefinition> machen. Ich hoffe, das hilft.

0

pls betrachten diesen Vorschlag:

Gebrauchte LinqToSQL um eine Tabelle 'dbo.Groups' abzubilden, die folgende Klasse erzeugt

hinzugefügt Dann folgt LinqExtension (mit freundlicher Genehmigung von 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; 
      } 
     } 
    } 
} 

endlich hinzugefügt folgenden Methoden, um meine Gruppe Klasse:

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

Wenn ich Ihre Frage verstanden, die genealogische Methode, die Sie helfen können.

+0

Es scheint ziemlich klar, dass er nach Eltern sucht, nicht nach Kindern. – Gabe

+0

Sie haben Recht, Gabe. Mit der Eigenschaft "Vorfahren" wurde meine Gruppenklasse in beide Richtungen navigierbar. Danke – vanorobe

0

Obwohl Steven Antwort wahrscheinlich besser ist, die Umsetzung Ich benutze eine Ancestors Eigenschaft auf dem Objekt:

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