BUY THIS BOOK
Add to Cart

Print Book $54.99


Add to Cart

Print+PDF $71.49

Add to Cart

PDF $43.99

Safari Books Online

What is this?

Add to UK Cart

Print Book £34.50

What is this?

Looking to Reprint or License this content?


C# 3.0 Cookbook
C# 3.0 Cookbook, Third Edition By Jay Hilyard, Stephen Teilhet

Cover | Table of Contents


Table of Contents

Chapter 1: Language Integrated Query (LINQ)
Language Integrated Query (LINQ) is a new way to access data from many different sources. LINQ provides a single querying model that can operate against different data domains individually or all together in a single query. LINQ brings the ability to query datato .NET languages, and some of the languages have provided extensions to make its use even more intuitive. One of these languages is C#; there are a number of extensions to the language in C# 3.0 that help to facilitate querying in a rich and intuitive manner.
Traditional object-oriented programming is based on an imperative style wherein the developer describes in detail not only what they want to happen, but also describes a majority of the detail regarding exactly how this should be performed through code. LINQ helps to take coding down a more declarative path that facilitates describing what the developer wants to do instead of describing how to accomplish the goal in detail. LINQ also enables a more functional style of programming. These changes can dramatically shorten the amount of code it takes to perform some tasks. That said, object-oriented programming is still very much alive and well in C# and .NET, but for the first time the language is offering the chance to choose the style of programming based on your needs. Note, however, that LINQ will not fit into every scenario and is not a replacement for good design or practice. You can write bad code using LINQ just as you can write bad object-oriented or procedural code. The trick, like it always has been, is to figure out when it is appropriate to use which technique.
The initial version of LINQ encompasses a number of data domains as listed here:
  • LINQ to Objects
  • LINQ to XML
  • LINQ to ADO.NET
  • LINQ to SQL / LINQ to DataSet / LINQ to Entities
There are a number of other "LINQ to" implementations currently under development, but these are Microsoft's initial offerings. A few of the others in development are LINQ to SharePoint, LINQ to LDAP, and even a LINQ to Amazon implementation. The only one of the initial Microsoft set that won't be ready immediately with the release of Visual Studio 2008 is LINQ to Entities, or the ADO.NET Entity Framework, as it is also known. LINQ to Entities will be released shortly after Visual Studio 2008.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Introduction
Language Integrated Query (LINQ) is a new way to access data from many different sources. LINQ provides a single querying model that can operate against different data domains individually or all together in a single query. LINQ brings the ability to query datato .NET languages, and some of the languages have provided extensions to make its use even more intuitive. One of these languages is C#; there are a number of extensions to the language in C# 3.0 that help to facilitate querying in a rich and intuitive manner.
Traditional object-oriented programming is based on an imperative style wherein the developer describes in detail not only what they want to happen, but also describes a majority of the detail regarding exactly how this should be performed through code. LINQ helps to take coding down a more declarative path that facilitates describing what the developer wants to do instead of describing how to accomplish the goal in detail. LINQ also enables a more functional style of programming. These changes can dramatically shorten the amount of code it takes to perform some tasks. That said, object-oriented programming is still very much alive and well in C# and .NET, but for the first time the language is offering the chance to choose the style of programming based on your needs. Note, however, that LINQ will not fit into every scenario and is not a replacement for good design or practice. You can write bad code using LINQ just as you can write bad object-oriented or procedural code. The trick, like it always has been, is to figure out when it is appropriate to use which technique.
The initial version of LINQ encompasses a number of data domains as listed here:
  • LINQ to Objects
  • LINQ to XML
  • LINQ to ADO.NET
  • LINQ to SQL / LINQ to DataSet / LINQ to Entities
There are a number of other "LINQ to" implementations currently under development, but these are Microsoft's initial offerings. A few of the others in development are LINQ to SharePoint, LINQ to LDAP, and even a LINQ to Amazon implementation. The only one of the initial Microsoft set that won't be ready immediately with the release of Visual Studio 2008 is LINQ to Entities, or the ADO.NET Entity Framework, as it is also known. LINQ to Entities will be released shortly after Visual Studio 2008.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Query a Message Queue
You want to be able to query for messages with specific criteria from an existing message queue.
Write a query using LINQ to retrieve messages using the System.Messaging.MessageQueue type:
 
	  // open an existing message queue
	  string queuePath = @".\private$\LINQMQ";
	  MessageQueue messageQueue = new MessageQueue(queuePath);
	      BinaryMessageFormatter messageFormatter = new BinaryMessageFormatter();

	  var query = from Message msg in messageQueue
	     // The first assignment to msg.Formatter is so that we can touch the
	     // Message object. It assigns the BinaryMessageFormatter to each message
	     // instance so that it can be read to determine if it matches the criteria.
	     // Next, a check is performed that the formatter was correctly assigned
	     // by performing an equality check, which satisfies the Where clause's need
	     // for a boolean result while still executing the assignment of the formatter.
	              where ((msg.Formatter = messageFormatter) == messageFormatter) &&
	                  int.Parse(msg.Label) > 5 &&
	                  msg.Body.ToString().Contains('D')
	              orderby msg.Body.ToString() descending
	              select msg;
	  // Check our results for messages with a label > 5 and containing a 'D' in the name
	    foreach (var msg in query) 
	    {
	        Console.WriteLine("Label: " + msg.Label + " Body: " + msg.Body); 
	    }
The query retrieves the data from the MessageQueue by selecting the messages where the Label is a number greater than 5 and the message body contains a capital letter "D". These messages are then returned sorted by the message body in descending order.
There are a number of new keywords in this code using LINQ that were not previously used to access a message queue:
var
Instructs the compiler to infer the type of the variable from the right side of the statement. In essence, the type of the variable is determined by what is on the right side of the operator separating the var keyword and the expression. This allows for implicitly typed local variables.
from
The from keyword sets out the source collection to query against and a range variable to represent a single element from that collection. It is always the first clause in a query operation. This may seem counterintuitive if you are used to SQL and expect select to be first, but if you consider that first we need what to work on before we determine what to return, it makes sense. If we weren't used to how SQL does this already, it would be SQL that seems counterintuitive.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Using Set Semantics with Data
You would like to work with your collections using set operations for union, intersections, exceptions, and distinct items.
Use the Set operators provided as part of the Standard Query Operators to perform those operations.
Distinct:
	IEnumerable<string> whoLoggedIn = 
	    dailySecurityLog.Where(logEntry => logEntry.Contains("logged in")).Distinct( );
Union:
	// Union
	Console.WriteLine("Employees for all projects");
	var allProjectEmployees = project1.Union(project2.Union(project3));
Intersection:
	// Intersect
	Console.WriteLine("Employees on every project");
	var everyProjectEmployees = project1.Intersect(project2.Intersect(project3));
Exception:
	Console.WriteLine("Employees on only one project");
	var onlyProjectEmployees = allProjectEmployees.Except(unionIntersect);
The Standard Query Operators are the set of methods that represent the LINQ pattern. This set includes operators to perform many different types of operations, such as filtering, projection, sorting, grouping, and many others, including set operations.
The set operations for the Standard Query Operators are:
  • Distinct
  • Union
  • Intersect
  • Except
The Distinct operator extracts all nonduplicate items from the collection or result set being worked with. Say, for example, that we had a set of strings representing login and logout behavior for a terminal services box for today:
 
	// Distinct
	string[] dailySecurityLog = {
	      "Bob logged in", 
	      "Bob logged out", 
	      "Bob logged in", 
	      "Bill logged in",
	      "Melissa logged in",
	      "Bob logged out",
	      "Bill logged out", 
	      "Bill logged in", 
	      "Tim logged in", 
	      "Scott logged in", 
	      "Scott logged out", 
	      "Dave logged in", 
	      "Tim logged out", 
	      "Bob logged in", 
	      "Dave logged out"};
From that collection, we would like to determine the list of people who logged in to the box today. Since people can log in and log out many times during the course of a day or remain logged in for the whole day, we need to eliminate the duplicate login entries. Distinct is an extension method on the System.Linq.Enumerable
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Reuse Parameterized Queries with LINQ to SQL
You need to execute the same parameterized query multiple times with different parameter values, but you want to avoid the overhead of parsing the query expression tree to build the parameterized SQL each time the query executes.
Use the CompiledQuery.Compile method to build an expression tree that will not have to be parsed each time the query is executed with new parameters:
	var GetEmployees =
	        CompiledQuery.Compile((Northwind db, string ac, string ttl) =>
	                   from employee in db.Employees 
	                   where employee.HomePhone.Contains(ac) &&
	                         employee.Title == ttl 
	                   select employee);

	Northwind dataContext = new Northwind(Settings.Default.NorthwindConnectionString);
The first time the query executes is when it actually compiles (where GetEmployees is called the first time in the foreach loop). Every other iteration in this loop and in the next loop use the compiled version, avoiding the expression tree parsing:
	foreach (var employee in GetEmployees(dataContext, "(206)", "Sales Representative"))
	{
	    Console.WriteLine("{0} {1}", 
	        employee.FirstName, employee.LastName); 
	}       

	foreach (var employee in GetEmployees(dataContext, "(71)", "Sales Manager"))
	{
	    Console.WriteLine("{0} {1}", 
	        employee.FirstName, employee.LastName);
	}
We used var for the query declaration, as it was cleaner, but what var actually is in this case is:
	Func<Northwind, string, string, IQueryable<Employees>>
which is the delegate signature for the lambda expression we created that contains the query. That's right, all this crazy new query stuff, and we just instantiated a delegate. To be fair, the Func delegate was brought about in the System namespace as part of LINQ, so do not dismay, we are still doing cool newstuff!
This illustrates that we are not returning an IEnumerable or IQueryable based result set from Compile, but rather an expression tree. This is the expression tree that represents the potential for a query rather than the query itself. Once we have that tree, LINQ to SQL then has to perform the conversion from the tree to actual SQL that can run against the database. Interestingly enough, if we had put a call to
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Sort Results in a Culture-Sensitive Manner
You want to ensure that when you sort in a query, the sort order is for an applicationspecific culture that may not be the same as the current thread's current culture.
Use the overload of the OrderBy query operator, which accepts a custom comparer in order to specify the culture in which to perform comparisons:
	// Create CultureInfo for Danish in Denmark.
	CultureInfo danish = new CultureInfo("da-DK");

	CultureStringComparer comparer = new CultureStringComparer(danish,CompareOptions.
	None);
	var query = names.OrderBy(n => n, comparer);
Handling localization issues such as sorting for a specific culture is a relatively trivial task in .NET if the current culture of the current thread is the culture you want to use. The framework classes that assist in handling culture issues in C# are accessed by including the System.Globalization namespace. This namespace would be included in order to make the code in the solution run. One example of not using the thread current culture would be in an application that needs to display a sorted list of words in Danish on a version of Windows XP that is set for U.S. English. The current thread in the application may have a CultureInfo for "en-US" and, by default, the sort order for OrderBy will use the current culture sort settings. To specify that this list should sort according to Danish rules, a bit of work is necessary in the form of a custom comparer:
	CultureStringComparer comparer = new CultureStringComparer(danish,CompareOptions.
	None);
The comparer variable is an instance of a custom comparer class CultureStringComparer defined as implementing the IComparer<T> interface specialized for strings. This class is used to provide the culture settings for the sort order:
	public class CultureStringComparer : IComparer<string>
	{
	    private CultureStringComparer() 
	    {
	    }

	    public CultureStringComparer(CultureInfo cultureInfo, CompareOptions options) 
	    {
	        if (cultureInfo == null) 
	            throw new ArgumentNullException("cultureInfo");

	        CurrentCultureInfo = cultureInfo; 
	        Options = options; 
	    }

	    public int Compare(string x, string y) 
	    {
	        return CurrentCultureInfo.CompareInfo.Compare(x, y, Options); 
	    }

	    public CultureInfo CurrentCultureInfo { get; set; }

	    public CompareOptions Options { get; set; }
	}
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Adding Functional Extensions for Use with LINQ
There are operations you perform on collections frequently that currently reside in utility classes. You would like to be able to have these operations be used on collections in a more seamless manner than having to pass the reference to the collection to the utility class.
Use extension methods to help achieve a more functional style of programming for your collection operations. For example, to add a weighted moving average calculation operation to numeric collections, implement a set of WeightedMovingAverage extension methods in a static class and then call them as part of those collections:
	decimal[] prices = new decimal[10] { 13.5M, 17.8M, 92.3M, 0.1M, 15.7M,
	                                     19.99M, 9.08M, 6.33M, 2.1M, 14.88M };
	Console.WriteLine(prices.WeightedMovingAverage());

	double[] dprices = new double[10] { 13.5, 17.8, 92.3, 0.1, 15.7,
	                                    19.99, 9.08, 6.33, 2.1, 14.88 };
	Console.WriteLine(dprices.WeightedMovingAverage());

	float[] fprices = new float[10] { 13.5F, 17.8F, 92.3F, 0.1F, 15.7F,
	                                  19.99F, 9.08F, 6.33F, 2.1F, 14.88F };
	Console.WriteLine(fprices.WeightedMovingAverage());

	int[] iprices = new int[10] { 13, 17, 92, 0, 15,
	                              19, 9, 6, 2, 14 };
	Console.WriteLine(iprices.WeightedMovingAverage());

	long[] lprices = new long[10] { 13, 17, 92, 0, 15,
	                                19, 9, 6, 2, 14 };
	Console.WriteLine(lprices.WeightedMovingAverage());
To provide WeightedMovingAverage for the full range of numeric types, methods for both the nullable and non-nullable numeric types are provided in the LinqExtensions class:
	public static class LinqExtensions
	{
	    public static decimal? WeightedMovingAverage(this IEnumerable<decimal?> source) 
	    {
	        if (source == null)
	            throw new ArgumentNullException("source");

	        decimal aggregate = 0.0M;
	        decimal weight;
	        int item = 1;
	        // count how many items are not null and use that
	        // as the weighting factor
	        int count = source.Count(val => val.HasValue);
	        foreach (var nullable in source)
	        {
	            if (nullable.HasValue)
	            {
	                weight = item / count;
	                aggregate += nullable.GetValueOrDefault() * weight;
	                count++;
	            }
	        } 
	        if (count > 0) 
	        {
	            return new decimal?(aggregate / count);
	        }
	        return null;
	    }
	    // The same method pattern as above is followed for each of the other 
	    // types and their nullable counterparts (double / double?, int / int?, etc.)


	    #region Extend Average...

	}
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Query and Join Across Data Repositories
You have two sets of data from different data domains, and you want to be able to combine the data and work with it.
Use LINQ to bridge across the disparate data domains. LINQ is intended to be used in the same manner across different data domains and supports combining those sets of data with join syntax.
To demonstrate this, we will join an XML file full of Categories with the data from a database (Northwind) with Products and combine the two to create a new set of data for product information that holds the product name, the category description, and the category name:
	Northwind dataContext = new Northwind(Settings.Default.NorthwindConnectionString);
	ProductsTableAdapter adapter = new ProductsTableAdapter();
	Products products = new Products();
	adapter.Fill(products._Products);

	XElement xmlCategories = XElement.Load("Categories.xml");

	var expr = from product in products._Products
	           where product.Units_In_Stock > 100
	           join xc in xmlCategories.Elements("Category")
	           on product.Category_ID equals int.Parse(xc.Attribute("CategoryID").Value)
	           select new
	           {
	               ProductName = product.Product_Name,
	               Category = xc.Attribute("CategoryName").Value,
	               CategoryDescription = xc.Attribute("Description").Value
	           };

	foreach (var productInfo in expr)
	{
	    Console.WriteLine("ProductName: " + productInfo.ProductName +
	        " Category: " + productInfo.Category + 
	        " Category Description: " + productInfo.CategoryDescription);
	}
The new set of data is printed to the console, but this could easily have been rerouted to another method, transformed in another query, or written out to a third data format:
	ProductName: Grandma's Boysenberry Spread Category: Condiments Category Description:
	Sweet and savory sauces, relishes, spreads, and seasonings
	ProductName: Gustaf's Knäckebröd Category: Grains/Cereals Category Description:
	Breads, crackers, pasta, and cereal
	ProductName: Geitost Category: Dairy Products Category Description: Cheeses
	ProductName: Sasquatch Ale Category: Beverages Category Description: Soft drinks,
	coffees, teas, beer, and ale
	ProductName: Inlagd Sill Category: Seafood Category Description: Seaweed and fish
	ProductName: Boston Crab Meat Category: Seafood Category Description: Seaweed and
	fish
	ProductName: Pâté chinois Category: Meat/Poultry Category Description: Prepared meats
	ProductName: Sirop d'érable Category: Condiments Category Description: Sweet and
	savory sauces, relishes, spreads, and seasonings
	ProductName: Röd Kaviar Category: Seafood Category Description: Seaweed and fish
	ProductName: Rhönbräu Klosterbier Category: Beverages Category Description: Soft
	drinks, coffees, teas, beer, and ale
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Querying Configuration Files with LINQ
Sets of data can be stored in many different locations, such as configuration files. You want to be able to query your configuration files for sets of information.
Use LINQ to query against the configuration sections. In the example below, this is done by retrieving all chapter titles with even numbers and the word "and" in the title from the custom configuration section containing chapter information:
	CSharpRecipesConfigurationSection recipeConfig =
	    ConfigurationManager.GetSection("CSharpRecipesConfiguration") as
	CSharpRecipesConfigurationSection;

	var expr = from ChapterConfigurationElement chapter in
	               recipeConfig.Chapters.OfType<ChapterConfigurationElement>()
	           where (chapter.Title.Contains("and")) && ((int.Parse(chapter.Number) % 2)
	== 0)
	           select new
	           {
	               ChapterNumber = "Chapter " + chapter.Number,
	               chapter.Title
	           };

	foreach (var chapterInfo in expr)
	{
	    Console.WriteLine(chapterInfo.ChapterNumber + ": " + chapterInfo.Title);
	}
The configuration section being queried looks like this:
	<CSharpRecipesConfiguration CurrentEdition="3">
	  <Chapters>
	    <add Number="1" Title="Language Integrated Query (LINQ)"/>
	    <add Number="2" Title="Strings and Characters"/>
	    <add Number="3" Title="Classes and Structures"/>
	    <add Number="4" Title="Generics"/>
	    <add Number="5" Title="Collections"/>
	    <add Number="6" Title="Iterators, Partial Types and Partial Methods"/>
	    <add Number="7" Title="Exception Handling"/>
	    <add Number="8" Title="Diagnostics"/>
	    <add Number="9" Title="Delegates, Events, and Functional Programming"/>
	    <add Number="10" Title="Regular Expressions"/>
	    <add Number="11" Title="Data Structures &amp; Algorithms"/>
	    <add Number="12" Title="Filesystem I/O"/>
	    <add Number="13" Title="Reflection"/>
	    <add Number="14" Title="Web"/>
	    <add Number="15" Title="XML"/>
	    <add Number="16" Title="Networking"/>
	    <add Number="17" Title="Security"/>
	    <add Number="18" Title="Threading and Synchronization"/>
	    <add Number="19" Title="Toolbox"/>
	    <add Number="20" Title="Numbers &amp; Enumerations"/>
	  </Chapters>
	  <Editions>
	    <add Number="1" PublicationYear="2004"/>
	    <add Number="2" PublicationYear="2006"/>
	    <add Number="3" PublicationYear="2007"/>
	  </Editions>
	</CSharpRecipesConfiguration>
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Creating XML Straight from a Database
You want to be able to take a set of data from a database and represent it as XML.
Use LINQ to SQL and LINQ to XML to retrieve and transform the data all in one query. In this case, we will select the top five customers in the Northwind database whose contact is the owner and those owners who placed orders totaling more than $10,000, then create XML containing the company name, contact name, phone number, and total amount of the orders. Finally, the results are written out to the BigSpenders.xml file:
	Northwind dataContext = new Northwind(Settings.Default.NorthwindConnectionString);
	// Log the generated SQL to the console
	dataContext.Log = Console.Out;

	var bigSpenders = new XElement("BigSpenders",
	            from top5 in
	            (
	               from customer in
	               (
	                   from c in dataContext.Customers
	                   // get the customers where the contact is the owner
	                   // and they placed orders
	                   where c.ContactTitle.Contains("Owner")
	                      && c.Orders.Count > 0
	                   join orderData in
	                     (
	                         from c in dataContext.Customers
	                         // get the customers where the contact is the owner
	                         // and they placed orders
	                         where c.ContactTitle.Contains("Owner")
	                            && c.Orders.Count > 0
	                         from o in c.Orders
	                         // get the order details
	                         join od in dataContext.OrderDetails
	                             on o.OrderID equals od.OrderID
	                         select new
	                         {
	                             c.CompanyName,
	                             c.CustomerID,
	                             o.OrderID,
	                             // have to calc order value from orderdetails
	                             //(UnitPrice*Quantity as Total)- (Total*Discount)
	                             // as NetOrderTotal
	                             NetOrderTotal = (
	                                 (((double)od.UnitPrice) * od.Quantity)
	                                 (((double)od.UnitPrice) * od.Quantity) * od.Discount))
	                         }
	                     )
	                   on c.CustomerID equals orderData.CustomerID
	                   into customerOrders
	                   select new
	                   {
	                       c.CompanyName,
	                       c.ContactName,
	                       c.Phone,
	                       // Get the total amount spent by the customer
	                       TotalSpend = customerOrders.Sum(order => order.NetOrderTotal)
	                   }
	               )
	               // place focus on the customers that spent > 10000
	               where customer.TotalSpend > 10000
	               orderby customer.TotalSpend descending
	               // only take the top five spenders
	               select customer).Take(5)
	            )
	            // format the data as XML
	            select new XElement("Customer",
	                       new XAttribute("companyName", top5.CompanyName),
	                       new XAttribute("contactName", top5.ContactName),
	                       new XAttribute("phoneNumber", top5.Phone),
	                       new XAttribute("amountSpent", top5.TotalSpend)));

	using (XmlWriter writer = XmlWriter.Create("BigSpenders.xml"))
	{
	    bigSpenders.WriteTo(writer);
	}
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Being Selective About Your Query Results
You want to be able to get a dynamic subset of a query result.
Use the TakeWhile extension method to retrieve all results until the criteria is matched:
	Northwind dataContext = new Northwind(Settings.Default.NorthwindConnectionString);

	var query =
	    dataContext.Suppliers.GroupJoin(dataContext.Products,
	        s => s.SupplierID, p => p.SupplierID,
	        (s, products) => new
	        {
	            s.CompanyName,
	            s.ContactName,
	            s.Phone,
	            Products = products
	        }).OrderByDescending(supplierData => supplierData.Products.Count())
	            .TakeWhile(supplierData => supplierData.Products.Count() > 3);


	Console.WriteLine("Suppliers that provide more than three products: {0}", query.
	Count());
	foreach (var supplierData in query)
	{
	    Console.WriteLine("    Company Name : {0}",supplierData.CompanyName);
	    Console.WriteLine("    Contact Name : {0}", supplierData.ContactName);
	    Console.WriteLine("    Contact Phone : {0}", supplierData.Phone);
	    Console.WriteLine("    Products Supplied : {0}", supplierData.Products.Count());
	    foreach (var productData in supplierData.Products)
	    {
	        Console.WriteLine("        Product: " + productData.ProductName);
	    }
	}
You can also use the SkipWhile extension method to retrieve all results once the criteria are matched:
	Northwind dataContext = new Northwind(Settings.Default.NorthwindConnectionString);

	var query =
	    dataContext.Suppliers.GroupJoin(dataContext.Products,
	        s => s.SupplierID, p => p.SupplierID,
	        (s, products) => new
	        {
	            s.CompanyName,
	            s.ContactName,
	            s.Phone,
	            Products = products
	        }).OrderByDescending(supplierData => supplierData.Products.Count())
	            .SkipWhile(supplierData =>
	      {
	        return supplierData.Products.Count() > 3;
	      });

	Console.WriteLine("Suppliers that provide three or less products: {0}",
	                 query.Count());
	foreach (var supplierData in query)
	{
	    Console.WriteLine("    Company Name : {0}",supplierData.CompanyName);
	    Console.WriteLine("    Contact Name : {0}", supplierData.ContactName);
	    Console.WriteLine("    Contact Phone : {0}", supplierData.Phone);
	    Console.WriteLine("    Products Supplied : {0}", supplierData.Products.Count());
	    foreach (var productData in supplierData.Products)
	    {
	        Console.WriteLine(" Product: " + productData.ProductName);
	    }
	}
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Using LINQ with Collections That Don't Support IEnumerable<T>
There are a whole bunch of collections that don't support the generic versions of IEnumerable or ICollection but that do support the original nongeneric versions of the IEnumerable or ICollection interfaces, and you would like to be able to query those collections using LINQ.
The type cannot be inferred from the original IEnumeration or ICollection interfaces, so it must be provided using either the OfType<T> or Cast<T> extension methods or by specifying the type in the from clause, which inserts a Cast<T> for you. The first example uses Cast<XmlNode> to let LINQ know that the elements in the XmlNodeList returned from XmlDocument.SelectNodes are of type XmlNode. For an example of how to use the OfType<T> extension method, see the Discussion section:
	// Make some XML with some types that you can use with LINQ
	// that don't support IEnumerable<T> directly
	XElement xmlFragment = new XElement("NonGenericLinqableTypes",
	                        new XElement("IEnumerable",
	                            new XElement("System.Collections",
	                                new XElement("ArrayList"),
	                                new XElement("BitArray"),
	                                new XElement("Hashtable"),
	                                new XElement("Queue"),
	                                new XElement("SortedList"),
	                                new XElement("Stack")),*
	                            new XElement("System.Net",
	                                new XElement("CredentialCache")),
	                            new XElement("System.Xml",
	                                new XElement("XmlNodeList")),
	                            new XElement("System.Xml.XPath",
	                                new XElement("XPathNodeIterator"))),
	                        new XElement("ICollection",
	                            new XElement("System.Diagnostics",
	                                new XElement("EventLogEntryCollection")),
	                            new XElement("System.Net",
	                                new XElement("CookieCollection")),
	                            new XElement("System.Security.AccessControl",
	                                new XElement("GenericAcl")),
	                            new XElement("System.Security",
	                                new XElement("PermissionSet"))));

	XmlDocument doc = new XmlDocument();
	doc.LoadXml(xmlFragment.ToString());

	// Select the names of the nodes under IEnumerable that have children and are
	// named System.Collections and contain a capital S and return that list in
	descending order
	var query = from node in doc.SelectNodes("/NonGenericLinqableTypes/IEnumerable/*").
	Cast<XmlNode>()
	            where node.HasChildNodes &&
	                node.Name == "System.Collections"
	            from XmlNode xmlNode in node.ChildNodes
	            where xmlNode.Name.Contains('S')
	            orderby xmlNode.Name descending
	            select xmlNode.Name;

	foreach (string name in query)
	{
	    Console.WriteLine(name);
	}
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Chapter 2: Strings and Characters
String usage abounds in just about all types of applications. The System.String type is a reference type, unlike System.Char, which is a value type and therefore derives from System.ValueType. The string alias is built into C# and can be used instead of the full name.
The Framework Class Library (FCL) does not stop with just the String class; there is also a System.Text.StringBuilder class for performing string manipulations and the System.Text.RegularExpressions namespace for searching strings. This chapter will cover the String class, the System.Text.StringBuilder class, and the Char structure.
The System.Text.StringBuilder class provides an easy, performance-friendly method of manipulating string objects. Even though this class duplicates much of the functionality of a String class, the StringBuilder class is fundamentally different in that the string contained within the StringBuilder object can actually be modified—you cannot modify a string object. However, this duplicated functionality provides a more efficient manipulation of strings than is obtainable by using the String class.
You have a variable of type char and wish to determine the kind of character it contains—a letter, digit, number, punctuation character, control character, separator character, symbol, whitespace, or surrogate character (i.e., Unicode characters with a value greater than 64K). Similarly, you have a string variable and want to determine the kind of character in one or more positions within this string.
To determine the value of a char, use the built-in static methods on the System.Char structure shown here:
	Char.IsControl          Char.IsDigit
	Char.IsLetter           Char.IsNumber
	Char.IsPunctuation      Char.IsSeparator
	Char.IsSurrogate        Char.IsSymbol
	Char.IsWhitespace
The following examples demonstrate how to use the methods shown in the Solution section in an extension method to return the kind of a character. First, create an enumeration to define the various types of characters:
	public enum CharKind
	{
	    Digit,
	    Letter,
	    Number,
	    Punctuation,
	    Unknown
	}
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Introduction
String usage abounds in just about all types of applications. The System.String type is a reference type, unlike System.Char, which is a value type and therefore derives from System.ValueType. The string alias is built into C# and can be used instead of the full name.
The Framework Class Library (FCL) does not stop with just the String class; there is also a System.Text.StringBuilder class for performing string manipulations and the System.Text.RegularExpressions namespace for searching strings. This chapter will cover the String class, the System.Text.StringBuilder class, and the Char structure.
The System.Text.StringBuilder class provides an easy, performance-friendly method of manipulating string objects. Even though this class duplicates much of the functionality of a String class, the StringBuilder class is fundamentally different in that the string contained within the StringBuilder object can actually be modified—you cannot modify a string object. However, this duplicated functionality provides a more efficient manipulation of strings than is obtainable by using the String class.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Determining the Kind of Character a Char Contains
You have a variable of type char and wish to determine the kind of character it contains—a letter, digit, number, punctuation character, control character, separator character, symbol, whitespace, or surrogate character (i.e., Unicode characters with a value greater than 64K). Similarly, you have a string variable and want to determine the kind of character in one or more positions within this string.
To determine the value of a char, use the built-in static methods on the System.Char structure shown here:
	Char.IsControl          Char.IsDigit
	Char.IsLetter           Char.IsNumber
	Char.IsPunctuation      Char.IsSeparator
	Char.IsSurrogate        Char.IsSymbol
	Char.IsWhitespace
The following examples demonstrate how to use the methods shown in the Solution section in an extension method to return the kind of a character. First, create an enumeration to define the various types of characters:
	public enum CharKind
	{
	    Digit,
	    Letter,
	    Number,
	    Punctuation,
	    Unknown
	}
Next, create the extension method that contains the logic to determine the kind of a character and to return a CharKind enumeration value indicating that type:
	static class CharStrExtMethods
	{
	    public static CharKind GetCharKind(this char theChar)
	    {
	        if (Char.IsLetter(theChar))
	        {
	            return CharKind.Letter;
	        }
	        else if (Char.IsNumber(theChar))
	        {
	            return CharKind.Number;
	        }
	        else if (Char.IsPunctuation(theChar))
	        {
	            return CharKind.Punctuation;
	        }
	        else
	        {
	            return CharKind.Unknown;
	        }
	    }
	}
The GetCharKind extension method performs a series of tests on a character using the Char type's built-in static methods. An enumeration of all the different types of characters is defined and is returned by the GetCharKind method.
If, however, a character in a string needs to be evaluated, use the overloaded static methods on the char structure. The following code modifies the GetCharKind extension method to operate on a string variable while accepting a character position in that string as an argument to the extension method. The character position determines which character in the string is evaluated:
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Controlling Case Sensitivity When Comparing Two Characters
You need to compare two characters for equality, but you need the flexibility of performing a case-sensitive or case-insensitive comparison.
Create extension methods on the char type and use the Equals instance method on the char structure to compare the two characters:
	static class CharStrExtMethods
	{
	    public static bool IsCharEqual(this char firstChar, char secondChar)
	    {
	        return (IsCharEqual(firstChar, secondChar, false));
	    }

	    public static bool IsCharEqual(this char firstChar, char secondChar,
	                                   bool caseSensitiveCompare)
	    {
	        if (caseSensitiveCompare)
	        {
	            return (firstChar.Equals(secondChar));
	        }
	        else
	        {
	            return (char.ToUpperInvariant(firstChar).Equals(
	                             char.ToUpperInvariant(secondChar)));
	        }
	    }
	public static bool IsCharEqual(this char firstChar, CultureInfo firstCharCulture,
	                                  char secondChar, CultureInfo secondCharCulture)
	   {
	       return (IsCharEqual(firstChar, firstCharCulture,
	               secondChar, secondCharCulture, false));
	   }
	public static bool IsCharEqual(this char firstChar, CultureInfo firstCharCulture,
	                                  char secondChar, CultureInfo secondCharCulture,
	                                  bool caseSensitiveCompare)
	   {
	       if (caseSensitiveCompare)
	       {
	           return (firstChar.Equals(secondChar));
	       }
	       else
	       {
	           return (char.ToUpper(firstChar, firstCharCulture).Equals
	                   (char.ToUpper(secondChar, secondCharCulture)));
	       }
	   }
	}
The first overloaded IsCharEqual extension method takes only one parameter, which is the character to be compared against the value contained in the current char instance. This extension method then calls the second IsCharEqual method with two parameters. The last parameter on this extension method call defaults to false so that when this method is called, you do not have to pass in a value for the caseSensitiveCompare
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Finding the Location of All Occurrences of a String Within Another String
You need to search a string for every occurrence of a specific string. In addition, the case sensitivity, or insensitivity, of the search needs to be controlled.
Using IndexOf or IndexOfAny in a loop, you can determine how many occurrences of a character or string exist as well as their locations within the string. To find each occurrence of a string in another string using a case-sensitive search, use the following code:
	using System;
	using System.Collections;
	using System.Collections.Generic;

	static class CharStrExtMethods
	{
	    public static int[] FindAll(this string matchStr, string searchedStr,
	                                int startPos)
	    {
	        int foundPos = -1; // -1 represents not found.
	            int count = 0;
	        List<int> foundItems = new List<int>();

	        do
	        {
	              foundPos = searchedStr.IndexOf(matchStr, startPos, StringComparison.
Ordinal);
	                  if (foundPos > -1)
	              {
	                  startPos = foundPos + 1;
	                  count++;
	                  foundItems.Add(foundPos);

	                      Console.WriteLine("Found item at position: " + foundPos.
ToString());
	              }
	          } while (foundPos > -1 && startPos < searchedStr.Length);

	          return ((int[])foundItems.ToArray());
	    }
	}
If the FindAll extension method is called with the following parameters:
	string data = "Red";
	int[] allOccurrences = data.FindAll("BlueTealRedredGreenRedYellow", 0);
the string "Red" is found at locations 8 and 19 in the string searchedStr. This code uses the IndexOf method inside a loop to iterate through each found matchStr string in the searchStr string.
To find a character in a string using a case-sensitive search, use the following code:
	static class CharStrExtMethods
	{
	    public static int[] FindAll(this char MatchChar, string searchedStr,
	                                 int startPos)
	    {
	             int foundPos = -1; // -1 represents not found.
	             int count = 0;
	             List<int> foundItems = new List<int>();

	             do
	             {
	                 foundPos = searchedStr.IndexOf(MatchChar, startPos);
	                 if (foundPos > -1)
	                 {
	                     startPos = foundPos + 1;
	                     count++;
	                     foundItems.Add(foundPos);

	                     Console.WriteLine("Found item at position: " + foundPos.ToString());
	                 }
	             } while (foundPos > -1 && startPos < searchedStr.Length);

	             return ((int[])foundItems.ToArray());
	    }
	}
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Controlling Case Sensitivity When Comparing Two Strings
You need to compare the contents of two strings for equality. In addition, the case sensitivity of the comparison needs to be controlled.
Use the Compare static method on the String class to compare the two strings. Whether the comparison is case-insensitive is determined by the third parameter of one of its overloads. For example:
	string lowerCase = "abc";
	string upperCase = "AbC";
	int caseInsensitiveResult = string.Compare(lowerCase, upperCase,
	  StringComparison.CurrentCultureIgnoreCase);
	int caseSensitiveResult = string.Compare(lowerCase,
	  StringComparison.CurrentCulture);
The caseSensitiveResult value is -1 (indicating that lowerCase is "less than" upperCase) and the caseInsensitiveResult is zero (indicating that lowerCase "equals" upperCase).
Using the static string.Compare method allows you the freedom to choose whether to take into account the case of the strings when comparing them. This method returns an integer indicating the lexical relationship between the two strings. A zero means that the two strings are equal, a negative number means that the first string is less than the second string, and a positive number indicates that the first string is greater than the second string.
By setting the last parameter of this method (the comparisonType parameter) to either StringComparison.CurrentCultureIgnoreCase or StringComparison.CurrentCulture, you can determine whether the Compare method takes into account the case of both strings when comparing. Setting this parameter to StringComparison.CurrentCulture forces a case-sensitive comparison; setting it to StringComparison.CurrentCulture-IgnoreCase forces a case-insensitive comparison. In the case of the overloaded version of the method with no comparisonType parameter, comparisons are always case-sensitive.
The "String.Compare Method" topic in the MSDN documentation.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Comparing a String to the Beginning or End of a Second String
You need to determine whether a string is at the head or tail of a second string. In addition, the case sensitivity of the search needs to be controlled.
Use the EndsWith or StartsWith instance method on a string object. Comparisons with EndsWith and StartsWith are always case-sensitive. The following code compares the value in the string variable head to the beginning of the string Test:
	string head = "str";
	string test = "strVarName";
	bool isFound = test.StartsWith(head, StringComparison.Ordinal);
The following example compares the value in the string variable tail to the end of the string test:
	string tail = "Name";
	string test = "strVarName";
	bool isFound = test.EndsWith(tail, StringComparison.Ordinal); 
In both examples, the isFound Boolean variable is set to true, since each string is found in test.
To do a case-insensitive comparison, employ the static string.Compare method. The following two examples modify the previous two examples by performing a case-insensitive comparison. The first is equivalent to a case-insensitive StartsWith string search:
	string head = "str";
	string test = "strVarName";
	int location = string.Compare(head, 0, test, 0, head.Length, true,
	                              System.Threading.Thread.CurrentThread.CurrentCulture);
The second is equivalent to a case-insensitive EndsWith string search:
	string tail = "Name";
	string test = "strVarName";
	if (tail.Length <= test.Length)
	{
	    int location = string.Compare(tail, 0, test, (test.Length - tail.Length),
	                                  tail.Length, true);
	}
	else
	{
	    location = -1;
	}
Note that with the last two examples that use the Compare method, if a zero is returned, then the search succeeded. If a -1 is returned, the search failed. The following code compares the value in the string variable head to the beginning of the string Test with case-sensitivity turned off:
	string head = "str";
	string test = "strVarName";
	bool isFound = test.StartsWith(head, true,
	                          System.Threading.Thread.CurrentThread.CurrentCulture);
The following example compares the value in the string variable
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Inserting Text into a String
You have some text (either a char or a string value) that needs to be inserted at a specific location inside of a second string.
Using the Insert instance method of the String class, a string or char can easily be inserted into a string. For example, in the code fragment:
	string sourceString = "The Inserted Text is here -><-";

	sourceString = sourceString.Insert(28, "Insert-This");
	Console.WriteLine(sourceString);
the string sourceString is inserted between the > and < characters in a second string. The result is:
	The Inserted Text is here ->Insert-This<-
Inserting a single character into sourceString between the > and < characters is shown here:
	string sourceString = "The Inserted Text is here -><-";
	char insertChar = '1';

	sourceString = sourceString.Insert(28, Convert.ToString(insertChar));
	Console.WriteLine(sourceString);
There is no overloaded method for Insert that takes a char value, so converting the char to a string of length one is the next best solution.
There are two ways of inserting strings into other strings, unless, of course, you are using the regular expression classes. The first involves using the Insert instance method on the String class. This method is also slower than the others since strings are immutable, and, therefore, a new string object must be created to hold the modified value. In this recipe, the reference to the old string object is then changed to point to the new string object. Note that the Insert method leaves the original string untouched and creates a new string object with the inserted characters.
To add flexibility and speed to your string insertions, use the Insert instance method on the StringBuilder class. This method is overloaded to accept all of the built-in types. In addition, the StringBuilder object optimizes string insertion by operating on mutable arrays of characters, lists of immutable strings, and other techniques to defer the creation of new immutable strings until the last possible moment. This insertion will modify the state of the array of characters within the StringBuilder object.
If you use the StringBuilder
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Removing or Replacing Characters Within a String
You have some text within a string that needs to be either removed or replaced with a different character or string. Since the replacing operation is somewhat simple, using a regular expression to aid in the replacing operation is not worth the overhead.
To remove a substring from a string, use the Remove instance method on the String class. For example:
	string name = "Doe, John";
	name = name.Remove(3, 1);
	Console.WriteLine(name);
This code creates a new string and then sets the name variable to refer to it. The string contained in name now looks like this:
	Doe John
If performance is critical, and particularly if the string removal operation occurs in a loop so that the operation is performed multiple times, you can instead use the Remove method of the StringBuilder object. The following code modifies the internal state of the StringBuilder object that the str variable references so that its value becomes 12345678:
	StringBuilder str = new StringBuilder("1234abc5678", 12);
	str.Remove(4, 3);
	Console.WriteLine(str);
To replace a delimiting character within a string, use the following code:
	string commaDelimitedString = "100,200,300,400,500";
	commaDelimitedString = commaDelimitedString.Replace(',', ':');
	Console.WriteLine(commaDelimitedString);
This code creates a new string and then makes the commaDelimitedString variable refer to it. The string in commaDelimitedString now looks like this:
	100:200:300:400:500
To replace a placeholding string within a string, use the following code:
	string theName = "Mary";
	string theObject = "car";
	string ID = "This <ObjectPlaceholder> is the property of <NamePlaceholder>.";
	ID = ID.Replace("<ObjectPlaceholder>", theObject);
	ID = ID.Replace("<NamePlaceholder>", theName);
	Console.WriteLine(ID);
This code creates a new string and then makes the ID variable refer to it. The string in ID now looks like this:
	This car is the property of Mary.
As when removing a portion of a string, you may, for performance reasons, choose to use the Replace method of the StringBuilder class instead. For example:
	string newName = "John Doe";

	StringBuilder str = new StringBuilder("name = <NAME>");
	str.Replace("<NAME>", newName);
	Console.WriteLine(str.ToString());
	str.Replace('=', ':');
	Console.WriteLine(str.ToString());

	str = new StringBuilder("name1 = <FIRSTNAME>, name2 = <FIRSTNAME>");
	str.Replace("<FIRSTNAME>", newName, 7, 12);
	Console.WriteLine(str.ToString());
	str.Replace('=', ':', 0, 7);
	Console.WriteLine(str.ToString());
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Encoding Binary Data As Base64
You have a byte[] representing some binary information, such as a bitmap. You need to encode this data into a string so that it can be sent over a binary-unfriendly transport, such as email.
Using the static method Convert.ToBase64String on the Convert class, a byte[] may be encoded to its String equivalent:
	using System;
	using System.IO;

	static class CharStrExtMethods
	{
	    public static string Base64EncodeBytes(this byte[] inputBytes)
	    {
	        return (Convert.ToBase64String(inputBytes));
	    }
	}
Converting a string into its base64 representation has several uses. It allows binary data to be embedded in nonbinary files such as XML, email messages, etc. Base64-encoded data can also be transmitted via HTTP, GET, and POST requests in a more compact format than hex encoding. It is important to understand that data that is converted to base64 format is only obfuscated, not encrypted. To securely move data from one place to another, you should use the cryptography algorithms available in the FCL. For an example of using the FCL cryptography classes, see .
The Convert class makes encoding between a byte[] and a String a simple matter. The parameters for this method are quite flexible. It provides the ability to start and stop the conversion at any point in the input byte array.
To encode a bitmap file into a string that can be sent to some destination, you can use the following code:
	byte[] image = null;
	using (FileStream fstrm = new FileStream(@"C:\WINNT\winnt.bmp",
	                                  FileMode.Open, FileAccess.Read))
	{
	    using (BinaryReader reader = new BinaryReader(fstrm))
	    {
	        image = new byte[reader.BaseStream.Length];
	        for (int i = 0; i < reader.BaseStream.Length; i++)
	        {
	            image[i] = reader.ReadByte();
	        }
	    }
	}
	string bmpAsString = image.Base64EncodeBytes();
The MIME standard requires that each line of the base64-encoded string be 76 characters in length. In order to send the bmpAsString string as an embedded MIME attachment in an email message, you must insert a CRLF on each 76-character boundary.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Decoding a Base64-Encoded Binary
Content preview·Buy PDF of this chapter|