none
LINQ-Abfrage mit geschachtelter Gruppierung in VB.NET RRS feed

  • Frage

  • 

    Hallo alle zusammen.

    Ich benötige in Visual Studio 2013 mit VB.NET eine mehrfach gruppierte LINQ-Abfrage auf eine ADO-Tabelle, die etwa so aussehen sollte:

    Dim Peoples = Ppls.Table.AsEnumerable()			           ' ADO
    Dim SomePeople = Ppls.OrderBy(Function(p) p("ID")).
                          OrderBy(Function(p) p("NAME2")).
      		      OrderBy(Function(p) p("NAME")).
                          GroupBy(Function(p) p("TOWN")).
                          GroupBy(Function(n) n("NAME"))
    For Each peopleGroup In SomePeople
        Console.WriteLine("Town : {0}", peopleGroup.Key)
        For Each place In peopleGroup
            Console.WriteLine("Peoples with name {0}:", place("NAME"))
            For Each people In place
                Console.WriteLine("Name {0} (ID = {1})", people("NAME2"), people("ID"))
            Next
        Next
    Next
    

    Damit sollte die Ausgabe etwa so aussehen:

    Town: London

    Peoples with Name Broke:

    Name Glen (ID = 282344)

    Name Irene (ID = 523121)

    Name Oscar (ID = 851234)

    Peoples with Name Wilde:

    Name Oscar (ID = 354321)

    Name Paul (ID = 769081)

    Peoples with Name ...

    Town: Birmingham

    Peoples with Name Glendel:

    Name Michael (ID = 625314)

    ...

    Die LINQ-Anweisung

    Dim SomePeople = Peoples.OrderBy(Function(p) p("ID")).
                             OrderBy(Function(p) p("NAME2")).
                             OrderBy(Function(p) p("NAME")).
                             GroupBy(Function(p) p("TOWN")).
                             GroupBy(Function(p) p("NAME"))
    

    dürfte vermutlich auch so aussehen:

    Dim SomePeople = Ppls.OrderBy(Function(p) p("ID")).
                          OrderBy(Function(p) p("NAME2")).
                          OrderBy(Function(p) p("NAME")).
                          GroupBy(Function(p) p("TOWN"), Function(p) p("NAME"))
    


    Leider funktioniert es so nicht, weil in der Anweisung

    Console.WriteLine("Peoples with name {0}:", place("NAME"))

    für "place('Name')" wegen "Option strict on" ein spätes Binden nicht zugelassen ist, was aber sicher auch nicht die eigentliche Fehlerursache sein dürfte, und in der Anweisung

    For Each people In place

    ist der Ausdruck "place" vom Typ Object und keine Auflistung.

    Ich weiß gar nicht mehr, was ich schon alles versucht habe, die Typen anzupassen, sowohl in der LINQ-Anweisung (Of ...) als auch in den For Each-Schleifen (As ...). Leider ist es mir nicht gelungen, die Fehler zu beseitigen. Vermutlich ist es ganz einfach und ich denke nur zu kompliziert ... oder das Brett vor meinem Kopf ist zu groß...

    Für eine Lösung oder wenigstens einen verwertbaren Denkanstoß wäre ich erfreut und sehr dankbar.

    Mittwoch, 31. Mai 2017 08:24

Antworten

  • Hi,
    ich würde das mit 2 LinQ-Ausdrücken lösen, z.B. so:

    Module Module01
    
      Sub Main()
        Try
          Dim c As New Demo
          c.Execute()
        Catch ex As Exception
          Console.Write(ex.ToString)
        End Try
        Console.Write("weiter mit Taste")
        Console.ReadKey()
      End Sub
    
      Class Demo
        Friend Sub Execute()
          Dim dt As New DataTable
          With dt
            With .Columns
              .Add("ID", GetType(Integer))
              .Add("NAME2", GetType(String))
              .Add("NAME", GetType(String))
              .Add("TOWN", GetType(String))
            End With
            With .Rows
              .Add(523121, "Irene", "Broke", "London")
              .Add(769081, "Paul", "Wilde", "London")
              .Add(282344, "Glen", "Broke", "London")
              .Add(625314, "Michael", "Glendel", "Birmingham")
              .Add(354321, "Oscar", "Wilde", "London")
              .Add(851234, "Oscar", "Broke", "London")
            End With
            .AcceptChanges()
          End With
    
          Dim Peoples = dt.AsEnumerable()                ' ADO
    
          For Each peopleGroup In Peoples.GroupBy(Function(p) p("TOWN"))
            Console.WriteLine("Town : {0}", peopleGroup.Key)
            For Each place In peopleGroup.GroupBy(Function(p) p("NAME"))
              Console.WriteLine("Peoples with name {0}:", place.Key)
              For Each people In place
                Console.WriteLine("Name {0} (ID = {1})", people("NAME2"), people("ID"))
              Next
            Next
          Next
        End Sub
      End Class
    
    End Module


    --
    Viele Grüsse
    Peter Fleischer (ehem. MVP)
    Meine Homepage mit Tipps und Tricks

    Mittwoch, 31. Mai 2017 19:24

Alle Antworten

  • Hi,
    ich würde das mit 2 LinQ-Ausdrücken lösen, z.B. so:

    Module Module01
    
      Sub Main()
        Try
          Dim c As New Demo
          c.Execute()
        Catch ex As Exception
          Console.Write(ex.ToString)
        End Try
        Console.Write("weiter mit Taste")
        Console.ReadKey()
      End Sub
    
      Class Demo
        Friend Sub Execute()
          Dim dt As New DataTable
          With dt
            With .Columns
              .Add("ID", GetType(Integer))
              .Add("NAME2", GetType(String))
              .Add("NAME", GetType(String))
              .Add("TOWN", GetType(String))
            End With
            With .Rows
              .Add(523121, "Irene", "Broke", "London")
              .Add(769081, "Paul", "Wilde", "London")
              .Add(282344, "Glen", "Broke", "London")
              .Add(625314, "Michael", "Glendel", "Birmingham")
              .Add(354321, "Oscar", "Wilde", "London")
              .Add(851234, "Oscar", "Broke", "London")
            End With
            .AcceptChanges()
          End With
    
          Dim Peoples = dt.AsEnumerable()                ' ADO
    
          For Each peopleGroup In Peoples.GroupBy(Function(p) p("TOWN"))
            Console.WriteLine("Town : {0}", peopleGroup.Key)
            For Each place In peopleGroup.GroupBy(Function(p) p("NAME"))
              Console.WriteLine("Peoples with name {0}:", place.Key)
              For Each people In place
                Console.WriteLine("Name {0} (ID = {1})", people("NAME2"), people("ID"))
              Next
            Next
          Next
        End Sub
      End Class
    
    End Module


    --
    Viele Grüsse
    Peter Fleischer (ehem. MVP)
    Meine Homepage mit Tipps und Tricks

    Mittwoch, 31. Mai 2017 19:24
  • Hallo Peter,

    vielen Dank erst einmal für Deine Mühe.

    Ja, so geht es.

    Wie Du an dem an Haaren herbeigezogenen Beispiel vielleicht gemerkt hast, ging es mir aber um mehr, mehr um einen Grundsatz.

    Zunächst möchte ich mit der Extension Method-Syntax die Übersichtlichkeit kurz und prägnant halten, sozusagen auf einen Blick, auch wenn das mit Function(irgendetwas) in VB.NET nicht ganz so elegant ist wie in C#.

    Außerdem hege ich die Hoffnung, mit dem Variieren von OrderBy und GroupBy die Zugriffszeiten durch Beeinflussung des Sortierungszeitpunkts in Abhängigkeit der konkreten Schachtelungstiefe und -reihenfolge gering halten zu können. Ohne über die Sinnhaftigkeit der konkreten folgenden Anweisungen nachgedacht zu haben, meine ich das etwa so:

    Dim SomePeople = Peoples.Select(Function(p) New With {p("ZIP"), p("TOWN"), p("AREA"), p("ID"), p("NAME"), p("NAME2"), p("BIRTHDAY")}).
                             OrderBy(Function(p) p("TOWN")).
                             GroupBy(Function(p) p("TOWN")).
                             OrderBy(Function(p) p("ID")).
                             OrderBy(Function(p) p("NAME2")).
                             OrderBy(Function(p) p("NAME")).
                             GroupBy(Function(p) p("NAME"))
    
    

    oder so

    Dim SomePeople = Peoples.Select(Function(p) New With {p("ZIP"), p("TOWN"), p("AREA"), p("ID"), p("NAME"), p("NAME2"), p("BIRTHDAY")}).
                             OrderBy(Function(p) p("TOWN")).
                             GroupBy(Function(p) p("TOWN")).
                             GroupBy(Function(p) p("NAME")).
                             OrderBy(Function(p) p("ID")).
                             OrderBy(Function(p) p("NAME2")).
                             OrderBy(Function(p) p("NAME"))

    oder wie auch immer.

    Wenn ich keinen Denkfehler habe und z.B. die Gruppen geordnet bearbeiten möchte, will ich vermeiden, die gesamte Tabelle oder Abfrage erst nach einem oder mehreren Schlüsseln zu ordnen bzw. umzuordnen. Zumindest auf oberster Ebene sollte das aus meiner jetzigen Sicht einen Zeitvorteil bringen, wenn ich also im Beispiel nicht erst die gesamte Tabelle sondern nur die Gruppen nach deren Schlüssel ordne (wenn damit nicht intern ohnehin zunächst erst einmal die gesamte Tabelle geordnet wird, was ich beinahe ahne... :-(( ).

    Mir wäre also die Extension Method-Syntax lieber, schon wegen der Übersichtlichkeit. Es muss auch so gehen! Es ist sicher nur eine Frage der Typisierung.

    Viele Grüße, Werner


    WM

    Donnerstag, 1. Juni 2017 09:53