Chapter 10
Mastering Select and SelectMany

In This Chapter

Image Exploring Select

Image Projecting New Types from Multiple Sources

Image Creating a New Sequence from Multiple Sequences with SelectMany

Image Using SelectMany with Indexes

Be kind, O Bacchus, take this empty pot offered to thee by Xenophon, the sot, Who, giving this, gives all that he has got.”

––Eratosthenes

We have used select in many of the preceding chapters. This chapter focuses attention on select through the conduit of many examples. This chapter provides several samples that demonstrate select using things such as external application programming interfaces (APIs), InteropServices, and a discussion of SelectMany. SelectMany demonstrates how to get data from multiple sequences.

Of critical importance in this chapter is a demonstration of a constructive way to use select in the ubiquitous business layer that many n-tier applications contain.

Exploring Select

Select is both an extension method and a LINQ keyword. Programmers often use select in LINQ queries, but sometimes they want to know the index of items in a sequence. This section has several code examples that demonstrate both variations of select, including an example in Listing 10.4 that shows how to use the extension method form to add the indexing capability of the select function.

Selecting with Function Call Effects

Having been an aspiring mathematician—but a poor scholar—I am familiar with a couple of interesting algorithms that can be used to calculate prime numbers and other related math algorithms. Two of them are the sieve of Eratosthenes for calculating prime numbers and the Euclidean algorithm for determining greatest common divisors (GCD). Listing 10.1 demonstrates both algorithms. The code demonstrates a brute force Prime calculator that indicates a number is prime if it has no prime factors—the GCD algorithm—and a slightly faster one that uses the “Sieve” algorithm. (See Figure 10.1 for the output from code.)

Figure 10.1 Partial output of the primes from 2000 to 4999.

Image

Listing 10.1 Select with Function Call Effects

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SelectDemo1
{
  class Program
  {
   static void Main(string[] args)
   {
     DateTime start = DateTime.Now;

     const long upper = 1000000;
     var numbers = new long[upper];
     for (long i = 2000; i < 5000; i++)
       numbers[i] = i+1;

     /* prohibitively slow */
     //var primes = from n in numbers
     //  where IsPrime(n)
     //  select n;

     // use Sieve of Eratosthenes; of course now we have primes
     BuildPrimes(upper);
     var primes = from n in numbers
                  where IsPrime2(n)
                  select n;
     DateTime stop = DateTime.Now;
     StringBuilder builder = new StringBuilder();
     Array.ForEach(primes.ToArray(), n=>builder.AppendFormat(“{0} ”, n));
     Console.Write(builder.ToString());
     Console.WriteLine(“Elapsed: {0}”, Elapsed(start, stop));
     Console.ReadLine();

   }

   /// <summary>
   /// Brute force prime tester, very slow.
   /// </summary>
   /// <param name=”v”></param>
   /// <returns></returns>
   private static bool IsPrime(long v)
   {
     if (v <= 1) return false;
     for (long i = 1; i < v; i++)
       if (Gcd(i, v) > 1)
         return false;
     return true;
   }

   /// <summary>
   /// Use the Sieve of Eratosthenes: no number is divisible
   /// by a number greater than its square root
   /// </summary>
   /// <param name=”v”></param>
   /// <returns></returns>
   private static bool IsPrime2(long v)
   {
     for(int i=0; i<Primes.Count; i++)
     {
       if(v % Primes[i] == 0) return false;
       if(Primes[i] >= Math.Sqrt(v)) return true;
     }

     return true;
   }

   private static List<long> Primes = new List<long>();
   private static void BuildPrimes(long max)
   {
     Primes.Add(2);
     if (max < 3) return;

     for (long i = 2; i <= max; i++)
     {
       if (IsPrime2(i))
         Primes.Add(i);
     }
   }

   /// <summary>
   /// Recursive Euclidean algorithm
   /// </summary>
   /// <param name=”num”></param>
   /// <param name=”den”></param>
   /// <returns></returns>
   private static long Gcd(long num, long den)
   {
     return den % num == 1 ? 1 :
       den % num == 0 ? num : Gcd(den % num, num);
   }

   private static string Elapsed(DateTime start, DateTime stop)
   {
     TimeSpan span = stop - start;
     return string.Format(“Days: {0}, Hours: {1}”,
     “Minutes: {2}, Seconds: {3}, Mils: {4}”,
     span.Days, span.Hours, span.Minutes, span.Seconds, span.Milliseconds);
  }
 }
}

In Listing 10.1, IsPrime or IsPrime2 is called in the where clause returning only prime numbers in the sequence. IsPrime uses Euclid’s algorithm, recursively testing all of the numbers from 1 to n as possible divisors. This version is obviously quite slow as n gets to be very large. (You could speed up IsPrime by only testing 1 to the square root of n, which is from Eratosthenes. This revision to IsPrime is left as an example.) The second version IsPrime2 requires that you build all of the primes by testing a stored list of primes, storing the Primes in a List<T> as you check candidates, and you only test the primes up to those that are less than or equal to the square root of the candidate number. The List<int> of primes is seeded with the first Prime number 2.

As a useful general utility, the function Elapsed can be used to see how long it takes for a function to complete. If you run IsPrime up to a relatively small number like 1,000,000, the GCD approach is very slow. Using Eratosthenes’ algorithm to calculate primes, the code runs much faster but still grinds to a halt after about 10,000,000 or so. In practice, cryptographers are looking for huge prime numbers that even supercomputers cannot crack using any brute force algorithm. That’s why very large primes are useful in public key encryption; not because they can’t be determined but because they can’t be determined in any reasonable time frame.

Manipulating Select Predicates

The select clause can be determined by calling functions. These predicates can also be manipulated directly. For example, if the from clause uses num in numbers, then select is not limited to using num as its operand. You can manipulate num in the select clause. Listing 10.2 uses a sequence of digits and multiplies each by 2 to yield a sequence of even numbers.

Listing 10.2 Multiply Select Predicate

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SelectDemo2
{
 class Program
 {
   static void Main(string[] args)
   {
     var numbers = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
     var toEvens = from num in numbers
                   select num * 2;
     Array.ForEach(toEvens.ToArray(), n => Console.WriteLine(n));
     Console.ReadLine();
   }
 }
}

Returning Custom Business Objects from a Data Access Layer

Although manipulating simple numbers is useful, a more common problem might be how to use LINQ in the style of programming employed for business applications today. That is, how do you use LINQ in what is commonly referred to as a business layer? The answer is that you define the query to construct a known type instead of an anonymous type, and you define the functions in the business tier to return an IEnumerable<T> object where T is the business entity.

Listing 10.3 is a bit longer than the first two listings in this chapter, but this example need something that is practical as a business entity. In Listing 10.3, a Supplier class is defined using automatic properties and an overloaded ToString method for dumping entity state. An extension method is defined in ExtendsReader to extend IDataReader. This approach permits treating Supplier as self-initializing but you wouldn’t necessarily have to convolute the entity class with ActiveX Data Objects (ADO) knowledge. ReadSupplier acts like a member of IDataReader and a generic delegate Func is used to supply all of that tedious null checking.

Finally, a DataAccess class provides a general ReadSuppliers method that reads all suppliers (from Northwind) and a second ReadSuppliers that accepts a generic Func delegate to filter suppliers. The parameterless ReadSuppliers returns an IEnumerable<Supplier> and is pretty plain vanilla ADO.NET code. ReadSuppliers with the predicate uses the first parameterless ReadSuppliers and then uses a LINQ query with the Where extension method and the predicate Func<Supplier, bool> to filter the resultset. (Recall that Chapter 5, “Understanding Lambda Expressions and Closures,” introduced generic delegates.) In the example, the consumer provides the predicate. The predicate to ReadSuppliers—s => s.Country == “USA ”—is provided in the main function of Listing 10.3.

Listing 10.3 Using LINQ to Return Custom Business Objects

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Data.SqlClient;
using System.Reflection;

namespace SelectDemo3
{
 class Program
 {
   static void Main(string[] args)
   {
     IEnumerable<Supplier> USSuppliers =
       DataAccess.ReadSuppliers(s => s.Country == “USA”);

     Array.ForEach(USSuppliers.ToArray(), s => Console.WriteLine(s));
     Console.ReadLine();
   }
 }

 public class Supplier
 {
   public int SupplierID{ get; set; }
   public string CompanyName{ get; set; }
   public string ContactName{ get; set; }
   public string ContactTitle{ get; set; }
   public string Address{ get; set; }
   public string City{ get; set; }
   public string Region{ get; set; }
   public string PostalCode{ get; set; }
   public string Country{ get; set; }
   public string Phone{ get; set; }
   public string Fax{ get; set; }
   public string HomePage { get; set; }

   public override string ToString()
   {
     StringBuilder builder = new StringBuilder();
     PropertyInfo[] props = this.GetType().GetProperties();

     // using array for each
     Array.ForEach(props.ToArray(), prop =>
       builder.AppendFormat(“{0} : {1}”, prop.Name,
         prop.GetValue(this, null) == null ? “<empty> ” :
         prop.GetValue(this, null).ToString() + “ ”));

     return builder.ToString();
   }
 }

 public static class DataAccess
 {
   private static readonly string connectionString =
     “Data Source=.\SQLEXPRESS;”+
     “AttachDbFilename=”C:\Books\Sams\”+
     “LINQ\Northwind\northwnd.mdf”;Integrated Security=True;”+
     “Connect Timeout=30;User Instance=True”;

   public static IEnumerable<Supplier>
     ReadSuppliers(Func<Supplier, bool> predicate)
   {
     IEnumerable<Supplier> suppliers = ReadSuppliers();
     return (from s in suppliers select s).Where(predicate);
   }

   //public static List<Supplier> ReadSupplier(Func)
   public static IEnumerable<Supplier> ReadSuppliers()
   {
     using(SqlConnection connection = new SqlConnection(connectionString))
     {
       connection.Open();
       SqlCommand command = new SqlCommand(“SELECT * FROM SUPPLIERS”,
connection);
       IDataReader reader = command.ExecuteReader();

       List<Supplier> list = new List<Supplier>();
       while(reader.Read())
       {
         list.Add(reader.ReadSupplier());
       }

       return list;

     }
   }
 }

 public static class ExtendsReader
 {
   static Func<string, IDataReader, string, string> SafeRead =
     (fieldName, reader, defaultValue) =>
       reader[fieldName] != null ?
       (string)Convert.ChangeType(reader[fieldName], typeof(string)) :
       defaultValue;

   public static Supplier ReadSupplier (this IDataReader reader)
   {
     Supplier Supplier = new Supplier();
     supplier.supplierID = reader.GetInt32(0);
     supplier.CompanyName = SafeRead(“CompanyName”, reader, ““);
     supplier.ContactName = SafeRead(“ContactName”, reader, ““);
     supplier.ContactTitle = SafeRead(“ContactTitle”, reader, ““);
     supplier.Address = SafeRead(“Address”, reader, ““);
     supplier.City = SafeRead(“City”, reader, ““);
     supplier.Region = SafeRead(“Region”, reader, ““);
     supplier.PostalCode = SafeRead(“PostalCode”, reader, ““);
     supplier.Country = SafeRead(“Country”, reader, ““);
     supplier.Phone = SafeRead(“Phone”, reader, ““);
     supplier.Fax = SafeRead(“Fax”, reader, ““);
     supplier.HomePage = SafeRead(“HomePage”, reader, ““);
     return supplier;
   }
 }
}

Remember to adjust the connection string for your copy of the Northwind sample database. The results of the query are shown in Figure 10.2.

Figure 10.2 The formatted U.S. Suppliers sent to the console.

Image

Using Select Indexes to Shuffle (or Unsort) an Array

The Select extension method has an implicit index variable. By using the Select extension method and expressing an input argument for the Lambda Expression, you can define a projection that includes the ordinal position of each element in a sequence. Listing 10.4 demonstrates the technique.

Listing 10.4 Using the Select Method’s Index to Create an Anonymous Type Containing an Index and a Random Number; Sort the Random Numbers and the Indexes Are Shuffled

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SortingWithSelectIndex
{
 class Program
 {
   static void Main(string[] args)
   {
     int[] cards = new int[52];

     Random rand = new Random();

     var shuffler = cards.Select((num, index) =>
       new { Key = index, Random = rand.Next() });

     Array.ForEach(shuffler.OrderBy(s=>s.Random).ToArray(),
       s => Console.WriteLine(s));
     Console.ReadLine();
   }
 }
}

The only drawback here is that you can’t use the index in the LINQ version of the code. For example

var shuffler = from card in cards select new {Key = index, Random = rand.Next()};

is not a valid LINQ query. However, you can simulate the index using the LINQ form of the call by defining an integer before the query and incrementing the integer each time the select clause executes. This code has the same effect as the code in Listing 10.4:

int index = 0;
var result = from n in nums
  select new
 {
     value = n,
     key = index++
 };

You can expand on this idea of randomizing, unsorting, or shuffling an array to form the basis of a card game, an element of which is the chance of the draw.

Forming the Basis of a Card Game Like Blackjack

A couple of years ago, during a Blackjack craze, I wrote a Blackjack for Windows game. The game was designed to hint at statistically correct plays for all combinations, for example, what to do with a pair of 3s against a dealer 6. (It has helped my Blackjack game.) A short while after that, a woman from Harrah’s casino in Biloxi asked permission to use it as a pillow favor—sort of a chocolate on the pillow—at the casino. The game was fun to write, and I was happy to share.

The game (refer to Figure 10.3) uses the cards.dll that is installed with Windows Solitaire and supports standard Blackjack play, including splitting, doubling-down, surrendering, holding, and hitting with hints that coach statistically perfect play.

Internally, the game combines Windows API calls to the cards.dll, combining InteropServices with GDI+. One small challenge was how to shuffle the cards. (There is no randomize array method in .NET, although for symmetry there should be an unsort behavior.) The solution: Define a structure with a random number and key. Increment the keys from 0 to 51 to represent the 52 cards, assign a random number to the random field, and by sorting the random field, scramble the cards. This actually takes a dozen lines of code or so.

Figure 10.3 A Blackjack game for Windows written by the author in .NET.

Image

The code in the rest of this section and its subsections demonstrates how to use LINQ to “shuffle” the cards, return a shuffled deck, and form the basis of a basic card game. The sample code in Listings 10.5, 10.6, and 10.7 contains code to import APIs from the cards.dll, use GDI+ with unmanaged API methods, draw cards, and shuffle a digital representation of a deck of cards. Let’s start with the Main function shown in Listing 10.5, which also contains all of the code together. (The subsections “Projecting New Types from Calculated Values,” “Importing DLLs,” and “Using GDI+ with the Windows API (or External DLL) Methods” elaborate on those subjects.)

A card is represented by a class of the same name. The class contains one each of the enumeration Face and Suit. There is also a Shuffler struct that is used to represent the ordered cards; sorting the random field unorders, or shuffles, the cards. For example, shuffler.key equal to 0 represents a card with the face of One, an ace, and the Club suit, 1 is the two of clubs, and so on.

In the Main function, an array of the Shuffler struct is created. The array contains 52 elements representing 52 cards. A simple loop is used to initialize the Shuffler.key to the values 0 through 51 and Shuffler.random to a random number. (It doesn’t matter if some of the random numbers are duplicated.) Next is a LINQ query ordering on Shuffler.random and projecting a new instance of Card for each element in the sequence. The actual Card objects Suit and Face are derived with arithmetic. Suit equals key divided by 13, the number of faces, and the Face is derived by key modulo 13. Every fourteenth card is in the next Suit.

Listing 10.5 Shuffling (and Displaying) a Deck of Cards

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Diagnostics;

namespace SortingCards
{
 class Program
 {
   public enum Face { One, Two, Three, Four, Five, Six, Seven,
     Eight, Nine, Ten, Jack, Queen, King };
   public enum Suit { Club, Diamond, Heart, Spade };

   private static int width;
   private static int height;

   public struct Shuffler
   {
     public int key;
     public int random;
   }

   public class Card
   {
     public Face Face { get; set; }
     public Suit Suit { get; set; }

     public override string ToString()
     {
       return string.Format(“{0} of {1}s”, Face, Suit);
     }
   }

   static void Main(string[] args)
   {
     const int MAX = 52;

     Random rand = new Random(DateTime.Now.Millisecond);
     Shuffler[] Shuffler = new Shuffler[MAX];

     for (int i = 0; i < MAX; i++)
     {
       shuffler[i].key = i;
       shuffler[i].random = rand.Next();
     }

     // elem.key contains card number randomized by
     // sorting on random numbers
     var shuffledCards = from s in Shuffler
       orderby s.random
       select new Card { Suit = (Suit)(s.key / 13), Face = (Face)(s.key % 13) };

     cdtInit(ref width, ref height);
     try
     {

       using (Form form = new Form())
       {
         form.Show();

         int i = 0;
         foreach (var card in shuffledCards)
         {
           Graphics graphics = form.CreateGraphics();
           graphics.Clear(form.BackColor);
           string text = string.Format(“Index: {0}, Card: {1}”,
             i++, card);
           graphics.DrawString(text, form.Font, Brushes.Black, 10F,
            (float)(height + 20));
           PaintFace(graphics, card, 10, 10, width, height);
           form.Update();
           System.Threading.Thread.Sleep(500);
         }
       }
     }
     finally
     {
       cdtTerm();
     }
     Console.ReadLine();
  }

  private static void PaintFace(Graphics g, Card card,
    int x, int y, int dx, int dy)
  {
     IntPtr hdc = g.GetHdc();
     try
     {
       int intCard = (int)card.Face * 4 + (int)card.Suit;
       cdtDrawExt(hdc, x, y, dx, dy, intCard, 0, 0);
     }
     catch (Exception ex)
     {
       Debug.WriteLine(ex);
     }
     finally
     {
       g.ReleaseHdc(hdc);
     }
   }

   [DllImport(“cards.dll”)]
   static extern bool cdtInit(ref int width, ref int height);

   [DllImport(“cards.dll”)]
   public static extern bool cdtDraw (IntPtr hdc, int x,
     int y, int card, int type, long color);

   [DllImport(“cards.dll”)]
   public static extern bool cdtDrawExt(IntPtr hdc, int x, int y, int dx,
     int dy, int card, int suit, long color);

   [DllImport(“cards.dll”)]
     public static extern void cdtTerm();
 }
}

At the end of Main, the cards library is initialized, a form is created, and each card is displayed for a half a second—Thread.Sleep(500)—in its new position. When all of the cards have been displayed, the cards library cleanup happens—cdtTerm is called. (Refer to Figure 10.4 for an example of the output to expect.)

Figure 10.4 Displaying shuffled cards from cards.dll. (Vista users might need to grab a copy of this DLL from XP or the web.)

Image

Projecting New Types from Calculated Values

The LINQ query that starts with var shuffledCards = from s in shuffler demonstrates a projection to a specific, as opposed to anonymous, type. By using compound initialization with named parameters, you can construct Card objects specifying the card Suit and Face.

In practice, modify the code to permit initialization with the value available, key.

In key’s setter in the Card class, add the logic to convert the key to the Suit and Face. This is just good form. Using a factory to create cards is an appropriate alternative, too.

Importing DLLs

Library methods are imported with the DllImportAtttribute and the name of the external DLL. They are also prefixed with static and extern keywords. For example, Listing 10.5 imports cdtInit, cdtdraw, cdtDrawExt, and cdtTerm from cards.dll.

There are a couple of challenges when using external, nonmanaged API calls. One challenge is discerning correct usage. In this example, the cards.dll was designed to expect a cleanup call to cdtTerm. A second challenge is to get the declaration statement right.

When the types are straightforward as is the case in this example, the declarations are also straightforward. Things get muddied up when the API takes function pointers. Then, you need to use the MarshalAsAttribute defined in System.Runtime.InteropServices on the parameter.

Calls to external DLLs like cards.dll can use raw Device Context (DC) handles in unmanaged ways. GDI+ uses a managed handle. The next section looks at how to convert a managed DC (from GDI+) to an unmanaged DC for GDI-based or older API calls.

Using GDI+ with Windows API (or External DLL) Methods

GDI+ uses an uncached managed version of a device context, or DC. Device contexts are essentially graphics objects in GDI+. Older Windows API methods were designed to use a raw, unmanaged DC. Therefore, if you need to pass a device context to unmanaged library code, you need to do so safely. The method PaintFace, excerpted from Listing 10.5, shows how you can request the DC from a graphics object, pass the DC to an external, old-style API method, and clean up the code in a finally block with Graphics.ReleaseHdc (shown separately in Listing 10.6).

Listing 10.6 Older-Style GDI API Methods Need a Raw Device Context

    private static void PaintFace(Graphics g, Card card, int x,
     int y, int dx, int dy)
   {
     IntPtr hdc = g.GetHdc();
     try
     {
       int intCard = (int)card.Face * 4 + (int)card.Suit;
       cdtDrawExt(hdc, x, y, dx, dy, intCard, 0, 0);
     }
     catch (Exception ex)
     {
       Debug.WriteLine(ex);
     }
     finally
     {
       g.ReleaseHdc(hdc);
     }
   }

Using Select to I-Cap Words

Writing books is a little like mind reading or forecasting. Part of the job is to figure out things the reader might want to do that they might not know how to do already. With many different levels of readers, different levels of code samples are devised. Some are easy and others are more challenging. The example demonstrates how you might convert the first letter of an array of words to an uppercase letter.

The literal string and subsequent splitting of that string is used here for convenience. The query starting with var icapped demonstrates how to capitalize the first character of a string.

Listing 10.6 I-Capping Words in an Array

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ICappingSelectDemo
{
 class Program
 {
   static void Main(string[] args)
   {
     // array of strings for convenience
     string wayfarism = “I’m still vertical”;

     // create to array of strings
     var values = wayfarism.Split(new char[] { ‘ ‘ },
       StringSplitOptions.RemoveEmptyEntries);

     // icap
     var icapped = from s in values
       select s.Replace(s[0], Convert.ToChar(s[0].ToString().ToUpper()));

     icapped.WriteLineAll();
     Console.ReadLine();
   }

 }

 public static class Extender
 {
   public static void WriteLineAll<T>(this IEnumerable<T> ary)
   {
     Array.ForEach(ary.ToArray(), item=>Console.WriteLine(item));
   }
 }
}

Many other excellent resources are available on the web, including blogs, such as Bill Blogs in C# at http://srtsolutions.com/blogs/billwagner/ and ScottGu’s (Scott Guthrie) Blog at http://weblogs.asp.net/scottgu/, as well as excellent resources with general articles like my column VB Today at http://www.codeguru.com, InformIT.com, and 101 LINQ Samples by Microsoft at http://msdn2.microsoft.com/en-us/vcsharp/aa336746.aspx. You are encouraged to explore these resources and share their (free) availability with peers.

If you devise some clever queries, consider blogging about your own solutions or even submitting them to great sites like MSDN, InformIT, devsource.com, developer.com, or codeguru.com. Many of these sites compensate the author of original material.

Projecting New Types from Multiple Sources

When a LINQ query uses elements from one or more source types to define a new type, this is referred to as a projection. The compiler emits a class, part of whose name is AnonymousType. (Although you can use reflection to determine a projected type’s name and use that name in code, it isn’t a recommended practice.)

There are two key concepts for projecting new types: the Select feature and the SelectMany. Both Select and SelectMany are extension methods in the .NET Framework, but only select is a LINQ keyword. The SelectMany method comes into play when you write queries that have multiple from clauses. Thus far, there have been many examples of select; Listing 10.7 demonstrates an implicit SelectMany. Listing 10.6 uses two classes: Customer and Order, and two collections containing instances of each. In the query that begins with var orderInfo, the query is selecting from customers and orders and correlating the objects on customer.ID and order.CustomerID. The presence of the clause select new {Name = customer.CompanyName, Item=order.ItemDescription} causes the compiler to emit a new class, an anonymous type referred to as a project.

Listing 10.7 The Presence of Two from clauses in the LINQ Query Causes the Compiler to Emit a Call to Enumerable.SelectMany and the select new Clause Emits a New Anonymous Type Called a Projection

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Data.SqlClient;

namespace SelectManyDemo
{
 class Program
 {
   public class Customer
   {
     public int ID{get; set;}
     public string CompanyName{get; set; }
   }

   public class Order
   {
     public int ID{get; set; }
     public int CustomerID{get; set; }
     public string ItemDescription{get; set;}
   }

   static void Main(string[] args)
   {
     List<Customer> customers = new List<Customer>{
       new Customer{ID=1, CompanyName=”Tom’s Toffees”},
       new Customer{ID=2, CompanyName=”Karl’s Coffees”}};

     List<Order> orders = new List<Order>{
       new Order{ID=1, CustomerID=1, ItemDescription=”Granulated Sugar”},
       new Order{ID=2, CustomerID=1, ItemDescription=”Molasses”},
       new Order{ID=3, CustomerID=2, ItemDescription=”French Roast Beans”},
       new Order{ID=4, CustomerID=2, ItemDescription=”Ceramic Cups”}};

     var orderInfo = from customer in customers
                     from order in orders
                     where customer.ID == order.CustomerID
                     select new {Name=customer.CompanyName,
                       Item=order.ItemDescription};

     Array.ForEach(orderInfo.ToArray(), o=>Console.WriteLine(
         “Company: {0}, Item: {1}”, o.Name, o.Item));
       Console.ReadLine();
   }
 }
}

Listing 10.7 has Customer and Order classes. A generic List<T> is instantiated for each. The LINQ query selects from each of the lists of customers and orders and joins on the customer ID with a where clause. The select statement projects a new type containing a customer name assigned to a Name property and the item description assigned to an Item property to create a new anonymous type.

The presence of more than one from clause is technically taking two sequences and converting them into a single sequence. This is the equivalent of a SQL join. (Recall that in SQL, you can join with the literal join or by using where predicates; the same is true for LINQ.)

The capability of LINQ to project new types—also called data shaping—is powerful. LINQ queries enable you to quickly and easily come up with brand-new types that previously hadn’t been defined in code, and mitigate the need to hand code classes for every possible combination of data you might need.

Creating a New Sequence from Multiple Sequences with SelectMany

Phishing is what people are trying to do when they send out an email or phony link to try to get usernames and passwords from unsuspecting people. Excessive pop-ups, Trojans, worms, and viruses are all other kinds of despicable things that people do with computers. In some very small way, these programs challenge application and operating system writers to do a better job, so in some way people who write these obnoxious programs are useful (as much as a flesh-eating bacteria is useful).

One thing many of these harmful bits of code have in common is that they attack the Windows Registry. So, as a computer programmer and a computer user, it is helpful to understand how the Registry works. In addition, it is helpful to understand how the Registry works as a general programming aid—for when you need to add useful information to the Registry.

Tip

.NET introduced code access security (CAS). One of the things code access security does is permit assigning permissions to code. That is, CAS permissions can prohibit code from specific sources from doing things. For example, code access security can prohibit downloaded code from accessing the Registry.

The Windows Registry is a big dictionary (or database) of sorts containing hierarchical name and value pairs. .NET makes accessing the Registry for reads and writes pretty straightforward. With LINQ, you can actually write queries to scan and explore the Registry quite easily.

Consider the following scenario. Your program uses Windows event logging to write application exceptions to the event log. To work when installed, it needs to create an event source in the Registry at HKEY_LOCAL_MACHINESYSTEMCurrentControlSetServicesEventlog.

.NET provides the System.Diagnostics.EventLog class and CreateEventSource for this purpose. But, what if you want to validate that the key was created? You’d need to access the Registry and look for the key. A failure to create the key might be due to a lack of code access permissions, but you wouldn’t know unless a check was performed. To solve the problem, you could write a query that searched for the required key after the installer was to have created it. If the key is not found, then the installed application won’t work.

Another scenario might be to scan the Registry looking for differences. For example, you might want to look for keys on one part of the Registry that should exist in another part, or vice versa. The following code (see Listing 10.8) compares two sections of the Registry—LocalMachine and CurrentUser—and uses an implied SelectMany with two from and two where clauses to find keys in common.

Listing 10.8 Comparing Two Sections of the Registry—LocalMachine and CurrentUser—Looking for Keys in Common Using an Implicit SelectMany with Two from and Two where Clauses

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Win32;

namespace SelectingRegistryKeys
{
 class Program
 {
   static void Main(string[] args)
   {
     var localMachineKeys = Registry.LocalMachine.OpenSubKey(“Software”).GetSub
KeyNames();
     var userKeys = Registry.CurrentUser.OpenSubKey(“Software”).GetSubKeyNames();

     // keys in common
     var commonKeys = from machineKey in localMachineKeys
                      where machineKey.StartsWith(“A”)
                      from userKey in userKeys
                      where userKey.StartsWith(“A”) &&
                      /*where*/ machineKey == userKey
                      select machineKey;

     Array.ForEach(commonKeys.ToArray(), key => Console.WriteLine(key));
     Console.ReadLine();
   }
 }
}

Using set operations as described in Chapter 9, “Performing Set Operations,” you could use extension methods like Distinct, Except, or Interest to find similarities or differences in the keys in the various Registry sections. (This is left as an exercise for the reader.)

Using SelectMany with Indexes

SelectMany is an extension method and is supported in LINQ with the keyword select and multiple source sequences. Like the Select extension method, the SelectMany extension method supports the generic delegate Func and indexes.

This chapter’s final example reuses some of the code from Listing 10.7 and an explicit SelectMany to assign a random customer number to the customers. You might want to do this if you don’t want to reveal information useful to hackers, such as the actual primary key IDs in your database’s schema.

Listing 10.9 Using the Extension Method SelectMany and Its Ability to Use an Implicit Index to Assign a Sequential Number to an Object in a Sequence (Rather Than Using the Primary Key Field)

using System;
using System.Linq;
using System.Collections.Generic;

namespace SelectManyWithIndex
{
  class Program
 {
   public class Customer
   {
     public int ID{get; set;}
     public string CompanyName{get; set; }
   }

   public class Order
   {
     public int ID{get; set; }
     public int CustomerID{get; set; }
     public string ItemDescription{get; set;}
   }

   static void Main(string[] args)
   {
     List<Customer> customers = new List<Customer>{
       new Customer{ID=1, CompanyName=”Tom’s Toffees”},
       new Customer{ID=2, CompanyName=”Karl’s Coffees”}};

     List<Order> orders = new List<Order>{
       new Order{ID=1, CustomerID=1, ItemDescription=”Granulated Sugar”},
       new Order{ID=2, CustomerID=1, ItemDescription=”Molasses”},
       new Order{ID=3, CustomerID=2, ItemDescription=”French Roast Beans”},
       new Order{ID=4, CustomerID=2, ItemDescription=”Ceramic Cups”}};

     var orderInfo =
       customers.SelectMany(
         (customer, index) =>
           from order in orders
           where order.CustomerID == customer.ID
           select new { Key = index + 1, Customer = customer.CompanyName,
     Item = order.ItemDescription });

     Array.ForEach(orderInfo.ToArray(), o=>Console.WriteLine(
         “Key: {0}, Name: {1}, Item: {2}”, o.Key, o.Customer, o.Item));
     Console.ReadLine();
   }
 }
}

The code in Listing 10.9 uses this overloaded version of SelectMany:


public static IEnumerable<TResult> SelectMany<TSource, TResult>(
  this IEnumerable<TSource> source,
  Func<TSource, int, IEnumerable<TResult>> selector

The static qualifier and this for the first parameter IEnumerable<TSource> indicates that the method is an extension method for IEnumerable<T>. The argument represented by this is actually the instance argument customers in the listing. The second parameter beginning with the generic delegate Func indicates that the first generic parameter is the input customer and the second is an index. The Lambda Expression

(customer, index) => from order in orders where order.CustomerID
  == customer.ID select new { Key = index + 1, Customer = customer.CompanyName,
  Item = order.ItemDescription }

satisfies the generic delegate Func and the expression body—the LINQ query—returns the result type IEnumerable<TResult> of the generic delegate.

It is important not to get behind on advanced subjects like generics because languages build complexity in layers. The very cryptic looking SelectMany overloaded method is a perfect (and somewhat intimidating) example of that layering of capabilities.

Summary

You can’t write a LINQ query without using select. So, you have seen many select statements in previous chapters, and you will see many more in the remaining chapters. But, the purpose of this book is to zoom our microscope in on a specific branch of the .NET Framework.

This chapter focused on exploring several examples using select and select many. You now have most of the basis and underpinnings mastered, so this part wraps up with an exploration of joins and exploring external objects models. Parts III and IV look at how LINQ works with ADO.NET and Extensible Markup Language (XML).

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.118.2.240