Silverlight Related Skills – Post 2

Yesterday, I wrote the first of a series of posts on skills related to effective Silverlight programming; specifically an introduction to LINQ. Today, with that material fresh, I’d like to take a giant leap forward and consider a number of advanced techniques that are common with Linq programmers.

To do this, I’ve extended and modified the Presidents class we were using, and created a second related class PresidentDetail.  For convenience, I’ve placed both in the file President.cs,

using System;
using System.Collections.Generic;

namespace LinqDemo2
{
  public class President
  {
    public string FirstName { get; set; }
    public string LastName { get; set; }

    public static List<President> GetPresidents()
    {
      List<President> presidents = new List<President>
          {
            new President() { FirstName = "George", LastName = "Washington" },
            new President() { FirstName = "John",   LastName = "Adams" },
            new President() { FirstName = "Thomas", LastName = "Jefferson" },
            new President() { FirstName = "James",  LastName = "Madison" },
            new President() { FirstName = "James",  LastName = "Monroe" }
          };
      return presidents;
    }
  }



  public class PresidentDetail
  {
    public string Name { get; set; }
    public string Portrait { get; set; }
    public string Birth { get; set; }
    public string Death { get; set; }
    public string PortraitPrefix = 
"http://www.whitehouse.gov/history/presidents/images/"; public static List<PresidentDetail> GetPresidentialDetail() { List<PresidentDetail> details = new List<PresidentDetail> { new PresidentDetail() { Name = "George Washington", Birth = "Feb 22, 1732", Death = "Dec 14, 1799", Portrait = "georgewashington.jpg"}, new PresidentDetail() { Name = "John Adams", Birth = "Oct. 30, 1735", Death = "July 4, 1826", Portrait = "johnadams.jpg"}, new PresidentDetail() { Name = "Thomas Jefferson", Birth = "April 13 1743", Death = "July 4, 1826", Portrait = "thomasjefferson.jpg"}, new PresidentDetail() { Name = "James Madison", Birth = "March 15, 1761", Death = "June 28, 1836", Portrait = "jamesmadison.jpg"}, new PresidentDetail() { Name = "James Monroe", Birth = "April 28, 1758", Death = "July 4, 1831", Portrait = "jamesmonroe.jpg"} }; return details; } } }

As you can see, each class comes with a static method that returns a typed List populated with instances of the class.

 

Page.xaml once again contains a TextBlock to display the LinqStatement and a DataGrid to display the data from the selection. Our selection, however, will consist of data from the two tables, based on a join. We’ll join the two tables based on the combined FirstName and LastName in the President table matching the Name in the PresidentDetail table. This done, it is time to return the results, but that raises a problem, only one object can be returned.

You could write

select President

or you could write

select detail

but you are not allowed to write

Select President, Detail

One solution is to create a temporary new class that combines the two,

 

 

 

public class PresidentAndDetails
{
  public string FirstName { get; set; }
  public string LastName { get; set; }
  public string Name { get; set; }
  public string Birth { get; set; }
  public string Death { get; set; }
  public string Portrait { get; set; }
}

You can then write your LinqStatement as follows:

PresidentAndDetails result =
  from president in presidents
    join detail in details on
      string.Format("{0} {1}", president.FirstName, 
        president.LastName)
      equals detail.Name
      orderby president.LastName, detail.Birth
      select new PresidentAndDetails { President = president, Detail = detail };

The syntax is somewhat similar to yesterday’s example except that we’ve added a join (based on the concatenation of first and last name from one object with the Name from the second) and, in the projection we’re actually instantiating a new object (of type PresidentAndDetails, and initializing the members.

Anonymous Types

It gives me the heebie jeebies to create a class, PresidentAndDetails just to have a return type for a single query and fortunately, C# 3 offers the alternative of anonymous types, which allow you to eschew creating the class before you need it, and allow you to avoid naming the class, but instead to create an implicit class whose type will be inferred by its use. Thus, the Linq statement above can be rewritten as follows (and the class PresidentAndDetails can be tossed):

var result =
  from president in presidents
    join detail in details on
     string.Format("{0} {1}", president.FirstName,
       president.LastName)
     equals detail.Name
     orderby president.LastName, detail.Birth
     select new { President = president, Detail = detail }

 

The significant change is that we’ve gotten rid of the definition of the class, and the select statement does not name the class; it is inferred by the naming of the members. Notice also that the return value is now assigned to a variable whose type is identified as var; the actual type is assigned when the result is generated, but it is still strongly typed.

Lambda Expressions

All of this is good, but not terribly radical, but when we add lambda expressions, C# as we know it suddenly takes a dramatic change. There is much to say about lambda expressions but the essential thing to know is this: delegates are a way to indirectly refer to methods. Lambda expressions are a way to write short in-line substitutions for the methods that delegates refer to.

Lambda expressions consist of three parts:

  • The Parameters
  • The Operator ( => )
  • The Result

Putting this together you might have a delegate that looks like this,

int someDelegate(int, int);

and you might use that delegate to refer to, for example, this method:

int myMethod(int a, int b)
{
  return a * b;
}

 

You could also use that delegate to refer to this lambda expression:

(a,b) => a*b

You read this aloud “a and b goto a times b” or, to be more explicit, “given the parameters a and b, you will get back the product of a and b”

If you have more than one parameter, you must enclose them in parentheses and separate them by commas, and the types of the parameters and the return type must match the delegate that supports the lambda expression.

Lambda expressions are incredibly powerful and useful in many situations, not the least of which is LINQ. They can greatly simplify our Linq statement. For example, our most recent query now becomes,

var result  = presidents.Join( details,
president => string.Format( "{0} {1}", 
  president.FirstName, president.LastName ),
detail => detail.Name,
( president, detail ) => new { 
  President = president, Detail = detail } )
.OrderBy( ca => ca.Detail.Name )
.ThenBy( ca => ca.Detail.Birth );

At first glance, this hardly looks like C# at all, but if we take it apart, it becomes much more reasonable.

var result indicates that the type of result will be decided at run time based on the strongly typed value inferred by running the query.

presidents.Join(details,    the beginning of a join between the presidents object and the details object

president => string.Format( “{0} {1}, president.FirstName, president.LastName },

a lambda expression indicating that given a president object, you’ll get back the string consisting of the first name and the last name

detail => detail.Name  similarly, a lambda expression indicating that given the detail object, you’ll get back the Name property of that object

put that together and your join says to join the president object and the detail object on the concatenated string and the Name property. If that matches, you now have the president object and the detail object and it is time to create your new anonymous type, which is what the next lambda expression does,

(president, detail) => new { President = president, Detail = detail }

read this as “given a president and a detail instance get back a new anonymous object initialized with two members. The president member is initialized to the value of the president object you have, the Detail member is initialized to the value of the detail object you have.

.OrderBy ( ca => ca.Detail.Name )  This order by phrase lends the anonymous object the temporary name ca (you can use any name). Within that temp object are two member objects, President and Detail and here you set Detail.Name as the property to order by.

Intellisense recognizes the anonymous type and its internal members,

IntellisenseAndAnonType2

of course, there’s nothing magical about the name “ca”….

IntellisenseAndAnonType3

Setting the Item Source

 

 

 

I’ll have to follow up, but for now it seems the ItemSource for the DataGrid is much happier with a non-anonymous type, thus I’ve modified the search and created a temporary class to hold the fields that I want to display,

public class PresResult
{
  public string Name { get; set; }
  public string Birth { get; set; }
  public string Death { get; set; }
}

 

The search and assignment to ItemsSource looks like this,

 var result  = presidents.Join( details,
 president => string.Format( "{0} {1}", 
   president.FirstName, president.LastName ),
 detail => detail.Name,
 ( president, detail ) => new PresResult()
 {
   Name = detail.Name,
   Birth = detail.Birth,
   Death = detail.Death
 } )
 .OrderBy( ca => ca.Name )
 .ThenBy( ca => ca.Birth );

 Results.ItemsSource = result;

and the DataGrid in Page.xaml looks like this,

<Data:DataGrid  x:Name="Results" 
  AutoGenerateColumns="False"
  Margin="5,0,0,0" 
  Width="400"
  Height="Auto"
  Grid.Row="2" 
  FontFamily="Georgia" 
  FontSize="12"
  Background="#FF8BB8F8" 
  AlternatingRowBackground="#009DF2ED">
  <Data:DataGrid.Columns>
    <Data:DataGridTextColumn FontSize="12" Width="120"  Header="Name" Binding="{Binding Name }" />
    <Data:DataGridTextColumn FontSize="12" Width="120" Header="Birth" Binding="{Binding Birth }" />
    <Data:DataGridTextColumn FontSize="12" Width="120" Header="Death" Binding="{Binding Death }" />
  </Data:DataGrid.Columns>
</Data:DataGrid>

When run, the application looks like this (red circle added)

RunningLinqApp

It is pretty amazing that 3 of the first five presidents died on July 4, two within hours of one another.

For more on Linq I highly recommend Professional Linq by Joseph Rattz and of course the Linq Project.

Share

About Jesse Liberty

Jesse Liberty is an independent consultant and programmer with three decades of experience writing and delivering software projects. He is the author of 2 dozen books and multiple Pluralsight courses, and has been a Senior Technical Evangelist for Microsoft, a Distinguished Software Engineer for AT&T, a VP for Information Services for Citibank and a Software Architect for PBS. He is a Xamarin Certified Mobile Developer and a Xamarin MVP, Microsoft MVP and Telerik MVP.
This entry was posted in z Silverlight Archives. Bookmark the permalink.

Leave a Reply

Your email address will not be published.