XTab's Blog

Ged Mead's Blog at vbCity

vbCity Blogs moved to:
http://cs.vbcity.com/blogs
  Home :: Syndication  :: Login

MayJune 2013Jul
SMTWTFS
2627282930311
2345678
9101112131415
16171819202122
23242526272829
30123456

Archives

Topics

Ramblings

VB.NET

   There's no doubt that LINQ is one of the headline features of Visual Studio Orcas.   I've been trying out some LINQ queries; it's a very interesting new area with massive potential.   It seems though that most books and articles available at the moment are C# based, so I thought I'd blog my journey through the Land of LINQ in case any other VB DotNetters might find it useful now or in the future.

   I'm currently using The March CTP of Orcas for the samples I will demonstrate in these blogs.  If you want to dip into LINQ but don't want to install the CTP or the Betas as they come on stream, you can get limited access to LINQ features in VB 2005 via the .NET LINQ Tech Preview (May 2006) .

The 10 Second Version of "What is LINQ?"

   I've seen answers to that question that run to several pages.  Hopefully by the end of this series of articles, you will have formed your own opinion, but to get us started, here's the 10 second version. 

   LINQ is a tool that will enable you to use the same coding techniques to run queries against a wide range of types of data storage - e.g. simple arrays,  collections of complex objects, XML and relational databases.    It looks a lot like SQL, but isn't actually SQL.  Many of the queries you can run against collections will be far less verbose than the hoops (and loops!) you would have to jump through to get the same results without LINQ.

   For now, this is enough information.   We will of course have to look into the whys and hows of LINQ as we dig deeper into the topic.   

Some Data To Work With

   The kind of data that is most appropriate for LINQ To Objects demos is something fairly basic but which still has enough content to be useful for the range of queries I plan to demonstrate. I wanted to stay away from the Products type of data because I want to use that in the LINQ to SQL article (probably utilising Northwind).   So in the end I decided on an old favourite for demos - the Person class.   This  Class has a couple of constructors and six Properties - Forename, Surname, DateOfBirth, Gender, HomeTown and Country.      If you want to see the code for this class, it is available here.

   Before we look at the first LINQ queries, I want to briefly run through some of the ways we create instances of objects (in this case Person instances) and, more importantly, how to create collections of those instances.


Creating Instances Of Complex Objects 

   In VB 2005 there are various ways of creating new instances of the Person Class.   The common ones are shown in the code sample below:
 

Code Copy HideScrollFull
Module CreateData
    Dim People As List(Of Person)
    Function CreateDataVB8_1() As List(Of Person)
        '  One of the VB8 ways
        People = New List(Of Person)

        Dim P As New Person
        P.Forename = "Peter"
        P.Surname = "Gray"
        P.Gender = Gender.Male
        P.Country = "UK"
        P.HomeTown = "Leeds"
        P.DateOfBirth = New DateTime(1966, 1, 12)
        People.Add(P)

        Return People
    End Function

    Function CreateDataVB8_2() As List(Of Person)
        '  One of the VB8 ways
        People = New List(Of Person)

        Dim P2 As New Person
        With P2
            .Forename = "Zoe"
            .Surname = "Gray"
            .Gender = Gender.Female
            .Country = "UK"
            .HomeTown = "Wakefield"
            .DateOfBirth = New DateTime(1969, 7, 3)
        End With
        People.Add(P2)
        Return People
    End Function

    Function CreateDataVB8_3() As List(Of Person)
        '  One of the VB8 ways - Overloaded Constructor
        People = New List(Of Person)

        Dim P3 As New Person("David", "Sedding", Gender.Male)
        People.Add(P3)
        P3 = New Person("Ricky", "Livid", Gender.Male)
        People.Add(P3)
        Return People
    End Function

End
Module
. . .

     VB9's language enhancements bring some more options in this area.  Some of them may seem to be low on the Wow! factor, but they become more important and more useful as you get further into the whole LINQ scenario.   

   One way is to create a new instance and assign values to properties at the same time:-
   

Code Copy HideScrollFull
Dim P4 As New Person With {.Forename = "Sandi", .Surname = "Skeet", _
       .DateOfBirth = #12/2/1962#, .Gender = Gender.Female, _
       .HomeTown = "Dover", .Country = "UK"}
 
 

     Note the use of the "With" keyword which is an extension of the current way of using With.

    Not a million miles away from achieving the same result with an overloaded constructor, you may think.  However, there are some subtle benefits.  One is that you can assign values to the properties in any order.   Another, possibly more useful, is that you can assign values to only those properties you are interested in at the time of initialization.    

Code Copy HideScrollFull
P4 = New Person With {.HomeTown = "Leeds", .Forename = "Ken", _
           .Surname = "Browning", _
           .Gender = Gender.Male, .DateOfBirth = #3/9/1978#}
. . .

     That said, these benefits are really a side product.   This style of object initialization has been built into VB9 for technical reasons that will become clear later.

Creating Collections Of Complex Object Instances

   As you know, you can create instances of the Person class and add them to a collection.   A List (Of Person) for example:

VB 2005

Code Copy HideScrollFull
Dim People As New List(Of Person)

   Dim P As New Person
        P.Forename = "Peter"
        P.Surname = "Gray"
        P.Gender = Gender.Male
        P.Country = "UK"
        P.HomeTown = "Leeds"
        P.DateOfBirth = New DateTime(1966, 1, 12)
People.Add(P)
. . .

VB 9 (ORCAS)

Code Copy HideScrollFull
Dim PersonList As New List(Of Person)
    '  Object Initializer way:
    Dim P4 As New Person With {.Forename = "Sandi", .Surname = "Skeet", _
       .DateOfBirth = #12/2/1962#, .Gender = Gender.Female, _
       .HomeTown = "Dover", .Country = "UK"}

   PersonList.Add(P4)
    '  Note that the Properties can be set in totally random order,
    '  unlike parameters.
    P4 = New Person With {.HomeTown = "Leeds", .Forename = "Ken", _
         .Surname = "Browning", .Gender = Gender.Male, _
         .DateOfBirth = #3/9/1978#, .Country = "UK"}

PersonList.Add(P4)

    The second example above is a kind of halfway house between VB8 and VB9.   There are other  ways available in VB9.   You can initialize New Person instances, assign values to any or all properties and then add that instance to the collection:

Code Copy HideScrollFull
Dim SomePeople() As Person = _
       {New Person With {.Forename = "Jan", .Surname = "Urqhart", _
         .DateOfBirth = #9/8/1959#, .Gender = Gender.Female, _
         .Country = "Belgium", .HomeTown = "Ghent"}, _
       New Person With {.Surname = "Fleischmann", .Forename = "Heinje", .Country = "Belgium", _
         .Gender = Gender.Male, .DateOfBirth = #7/11/1982#, .HomeTown = "Mons"}}
 

      The With keyword is used again in the above example to assign values to properties.  You will see that it creates an array of Person instances and that I included the identifier "As Person" in the first line.    However, VB9 goes further and the compiler is able to infer what Type the array will contain by looking at the Type you populate it with.   So the following version of the above snippet will compile without error and the SomePeople array will be correctly typed as Person type.

Code Copy HideScrollFull
Dim SomePeople() = _
       {New Person With {.Forename = "Jan", .Surname = "Urqhart", _
         .DateOfBirth = #9/8/1959#, .Gender = Gender.Female, _
         .Country = "Belgium", .HomeTown = "Ghent"}, _
       New Person With {.Surname = "Fleischmann", .Forename = "Heinje", .Country = "Belgium", _
         .Gender = Gender.Male, .DateOfBirth = #7/11/1982#, .HomeTown = "Mons"}}
. . .

    In case you were wondering if this is Late Binding the array to the Person Type, this is definitely not the case.     It's important to note that this array is Early Bound.   This feature is known as Local Type Inference, one of the language enhancements in VB9.   And, as with the new initialization techniques, the underlying reason for this feature being included is also for technical reasons to help make LINQ work and not just to add yet one more way of creating a new collection.

At Last! A LINQ Query Example

   Although we still have several important language enhancements to cover, we can at this stage run a simple LINQ query.   A LINQ query is a sequence of clauses, each clause containing a Query Operator.   Query Operators include From, Where, Select, Join, Group By, etc, all of which are familiar sounding keywords if you have used SQL. 

   The following example creates and initializes an array of Person instances using the above technique.  It then runs a simple LINQ query to find all Persons who have a birthday this month.

Code Copy HideScrollFull
        '  Create Data
        Dim PersonList() = _
          {New Person With {.Forename = "Jan", .Surname = "Urqhart", _
             .DateOfBirth = #6/6/1959#, .Gender = Gender.Female, _
             .Country = "Belgium", .HomeTown = "Ghent"}, _
           New Person With {.Surname = "Fleischmann", .Forename = "Heinje", _
             .Country = "Belgium", .Gender = Gender.Male, _
             .DateOfBirth = #5/15/1982#, .HomeTown = "Mons"}, _
           New Person With {.Forename = "Sandi", .Surname = "Skeet", _
             .DateOfBirth = #6/15/1962#, .Gender = Gender.Female, _
             .HomeTown = "Dover", .Country = "UK"}, _
           New Person With {.HomeTown = "Leeds", .Forename = "Ken", .Surname = "Browning", _
              .Gender = Gender.Male, .DateOfBirth = #5/9/1978#, .Country = "UK"}}

        '  Run LINQ Query
        Dim BirthdayPeople = From p In PersonList _
            Where p.DateOfBirth.Month = Now.Month _
            Select p

        '  Display results of query
        If BirthdayPeople.Count > 0 Then
            For Each p In BirthdayPeople
                Console.WriteLine("Happy Birthday,  " & p.Forename)   '& "  :  " & p.DateOfBirth.ToLongDateString)
            Next
        Else
            Console.WriteLine("No Birthdays this month")
        End If
. . .

      Note that the query again uses Local Type Inference to decide that the Type of the collection is Person as it creates BirthdayPeople and also that p is a Person Type, because p is an element in a list of Persons.

   The query itself is very simple;  using the Where query operator it filters in any Person instance where the  DateOfBirth's month property is the same as the current month.   

   The final line of this query uses the Select Operator to project or collect the matching entries into the BirthdayPeople collection.   Note the order of "Where" and "Select" in this LINQ query; it is the reverse of the syntax you would usually use for SQL queries against a database.  This is one of the possible Gotchas you will need to keep in mind as you begin using LINQ, but you will in fact find that the IDE syntax editor will bark at you if try using "Select" before "Where".

   Once the query has been run, the final code block displays the results in a console window. 

Next Steps 

   A very basic LINQ  query there which, to be fair, would be just as easy to create using a simple For Next Loop.

Code Copy HideScrollFull
  Dim BirthdayPeople As New List(Of Person)
        For Each P As Person In People
            If P.DateOfBirth.Month = Now.Month Then
                BirthdayPeople.Add(P)
            End If
        Next

        If BirthdayPeople.Count > 0 Then
            For Each bp As Person In BirthdayPeople
                Console.WriteLine("Happy Birthday,  " & bp.Forename)
            Next
        End If
. . .

       So, if at this point you are asking yourself  what all the fuss and excitement is about, don't worry, there is more,  so much more, to come.  This relatively slow start is just meant to set the scene.    What about a query that selects all Males who do not live in the UK,  grouped by Country, sorted ascending by Surname then Forename?  Try that with a For Next Loop or two  

   Or a query that selects all Persons over the age of 21, use a Join to a second collection which contains the names of towns where courses are currently available and produces a list of names of eligible people to invite to a course in their home town.   Grouped by Towns.   Sorted alphabetically.   Now, once you start to move into that kind of territory you can see how powerful LINQ queries will be.

   We will look at more complex LINQ To Objects queries in the next part of this article.

posted on Sunday, April 29, 2007 5:06 PM