2010-12-08 6 views
3

Ich würde sowohl C# und VB.NET Vorschlag nehmen.LINQ to SQL, um übergeordnete und Anzahl Childs in Hierarchie

Ich verwende LINQ, um Daten abzufragen. Ich versuche, das übergeordnete Element abzufragen und die untergeordneten Tags zu zählen.

Hier ist meine Stichworte Tabellenspalte:

TagId (int primary) 
TagName 
ParentId (int Allow NULL referred to TagId column) 

Hier einige Beispieldaten:

TagId, TagName, ParentId 

1, Web Design, NULL 
2, HTML, 1 
3, Programming, NULL 
4, CSS 3, 1 

Frage 1: In meiner Abfrage-Ergebnis, ich alle Eltern-Tags mit der Summe abfragen möchten von Kind-Tags Etwas wie folgt aus:

Web Design (2 sub tags) 
Programming (0 sub tags) 

Frage 2: Wenn ein Kind Tag auch, sein eigenes Kind-Tag hat

Hier einige Beispieldaten:

TagId, TagName, ParentId 

1, Web Design, NULL 
2, HTML, 1 
3, Programming, NULL 
4, CSS 3, 1 
5, HTML 4, 2 
6, HTML 5, 2 

Wunschabfrageergebnis:

Web Design (4 sub tags) 
Programming (0 sub tags) 

Die Frage Nummer 2 ist optional bu t es wird sehr gut sein, wenn Sie auch einen Vorschlag machen. Vielen Dank.

+0

Haben Sie eine Lösung für das Problem gefunden? Hast du meine Lösung versucht? –

+0

Noch nicht, schaue ich mich immer noch um.Es gibt einen Artikel über die Erweiterungsmethode LINQ AsHierarchy() von Stefan Cruysberghs @ http://www.scip.be/index.php?Page=ArticlesNET23 Wie auch immer, ich werde Ihren Vorschlag auch versuchen. Irgendeine VB.NET-Version Ihres Vorschlags? Wie auch immer, ich werde versuchen, es in VB.NET konvertieren – Narazana

+0

Ich aktualisierte meine Antwort mit wirklich guter Lösung. – abatishchev

Antwort

0

Definition:

class Tag 
{ 
    public int Id { get; set; } 
    public string TagName { get; set; } 

    public int? ParentId { get; set; }  

    public IEnumerable<Tag> Children { get; set; } 
} 

Daten:

int id = 0; 
var tags = new[] 
{ 
    new Tag 
    { 
     Id = ++id, 
     TagName = "Parent", 
     ChildNodes = new[] 
     { 
      new Tag { TagName = "Child1", Id = ++id, ParentId = 1 }, 
      new Tag { TagName = "Child2", Id = ++id, ParentId = 1 } 
     } 
    } 
    new Tag 
    { 
     Id = ++id, 
     TagName = "NoChildren" 
    } 
}; 

heißt

1, Parent, null 
2, Child1, 1 
3, Child2, 1 
4, NoChildren, null 

Code:

var q = from tag in tags 
     where tag.ParentId == null 
     select new 
     { 
      Name = tag.TagName, 
      ChildrenCount = tag.Children.Count() 
     }; 

foreach (var entry in q) 
{ 
    Console.WriteLine("{0} ({1} sub tags)", entry.Name, entry.ChildrenCount); 
} 

Ausgang:

Parent (2 sub tags) 
NoChildren (0 sub tags) 

Bei komplexen Hierarchie, alle Kinder zu bekommen Knoten können rekursiv nächsten Erweiterungen Methode verwenden:

public static IEnumerable<Tag> GetChildTags(this Tag tag) 
{ 
    var children = tag.Children ?? Enumerable.Empty<Tag>(); 
    return children.SelectMany(c => GetChildTags(c)).Concat(children); 
} 
0

Ok, der einfachste Weg ist, eine einfache Struktur zu erstellen, wo Sie Ihre Tags verbinden, wenn sie Eltern haben, und dann iteriert durch die Kinder, um die Anzahl pro Tag zu produzieren.

class Tag 
{ 
    public Tag(int id, int? parentId, string tag) 
    { 
     Id = id; 
     ParentId = parentId; 
     TagName = tag; 
    } 

    public int Id { get; set; } 
    public int? ParentId { get; set; } 
    public string TagName { get; set; } 
} 

class TagNode 
{ 
    public Tag Node { get; set; } 
    public IList<TagNode> ChildNodes { get; set; } 
    public int ChildNodeCount() 
    { 
     int count = 0; 
     if (ChildNodes != null) 
     { 
      foreach (var node in ChildNodes) 
      { 
       count += node.ChildNodeCount(); 
      } 
      count += ChildNodes.Count; 
     } 
     return count; 
    } 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     var tags = new List<Tag>(); 
     tags.Add(new Tag(1, null, "Web design")); 
     tags.Add(new Tag(2, null, "Programming")); 
     tags.Add(new Tag(3, 1, "HTML")); 
     tags.Add(new Tag(4, 1, "CSS 3")); 
     tags.Add(new Tag(5, 3, "HTML 4")); 
     tags.Add(new Tag(6, 3, "HTML 5")); 

     IList<TagNode> nodes = tags.Select(y => new TagNode { Node = y, ChildNodes = new List<TagNode>() }).ToList(); 
     foreach (var node in nodes) 
     { 
      if (node.Node.ParentId.HasValue) 
       ConnectNodeToParent(nodes, node); 
     } 

     // print all nodes 
     Console.WriteLine("=== ALL NODES ==="); 
     nodes.ToList().ForEach(PrintNode); 

     // print root nodes 
     Console.WriteLine(Environment.NewLine + "=== ROOT NODES ==="); 
     nodes.Where(y => y.Node.ParentId.HasValue == false).ToList().ForEach(PrintNode); 

     Console.ReadLine(); 
    } 

    private static void PrintNode(TagNode node) 
    { 
     Console.WriteLine("Tag id: {0}, Tag name: {1}, Tag count: {2}", node.Node.Id, node.Node.TagName, node.ChildNodeCount()); 
    } 

    private static void ConnectNodeToParent(IList<TagNode> nodes, TagNode node) 
    { 
     var parentNode = nodes.Where(y => y.Node.Id == node.Node.ParentId.Value).Single(); 
     parentNode.ChildNodes.Add(node); 
    } 
} 

Mit dem obigen Code erhalten Sie die Informationen pro Tag und nicht nur die "Eltern" -Tags.

+0

Soweit die Eigenschaften von Tag Public Setter haben, macht Konstruktor Existenz keinen Sinn: Sie können stattdessen 'Objektinitialisierer' verwenden. Oder entferne Setter. – abatishchev

+0

Sie konzentrieren sich auf die falsche Sache mit dem ersten Kommentar, das Objekt dient nur zur Demonstration. Der zweite Kommentar ist jedoch ein guter. –

+0

Sie können auch 'SelectMany()' und 'Count()' anstelle von foreach-Schleife in 'ChildNodeCount()' Methode verwenden – abatishchev

1
public static IEnumerable<T> SelectDescendents<T>(this IEnumerable<T> source, Func<T, IEnumerable<T>> selector) 
{ 
    foreach(var item in source) 
    { 
    yield return item; 
    foreach(T item2 in SelectDescendents(selector(item), selector)) 
    yield return item2; 
    } 
} 

Edit: Verbrauch:

Tags.where(t => t.tagID == 1).SelectDescendents(d => 
    Tags.where(t.parentID == d.TagId)).Count(); 
+0

Oder für eine SQL-Abfrage verwenden Sie ein CTE: http: // stackoverflow. com/questions/2091050/How-to-Use-Cte-zu-Karte-Eltern-Kind-Beziehung – Magnus