- Integrated
- Unitive
- Extensible Provider Model
- Declarative: Not How, But What
- Hierarchical
- Composable
- Transformative
- Summary
Extensible Provider Model
In this text I have tended to define LINQ as a tool for querying SQL, XML, and the collections in a program. Strictly speaking, this is not an accurate description of LINQ. Although such a view is useful when you first encounter LINQ, it needs to be abandoned if you want to gain deeper insight. LINQ is not designed to query any particular data source; rather, it is a technology for defining providers that can be used to access any arbitrary data source. LINQ happens to ship with providers for querying SQL, XML, and objects, but this was simply a practical decision, not a preordained necessity.
LINQ provides developers with a syntax for querying data. This syntax is enabled by a series of C# 3.0 and C# 2.0 features. These include lambdas, iterator blocks, expression trees, anonymous types, type inference, query expressions, and extension methods. All of these features are covered in this book. For now you need only understand that they make LINQ possible.
When Visual Studio 2008 shipped, Microsoft employees frequently showed the image shown in Figure 3.1. Although people tend to think of LINQ as a means of enabling access to these data sources, this diagram actually depicts nothing more than the set of LINQ providers that were implemented by Microsoft at the time Visual Studio shipped. Granted, the team carefully planned which providers they wanted to ship, but their decisions were based on strategic, rather than technical, criteria.
Figure 3.1 VB and C# ship with LINQ providers for databases, XML, and data structures found in a typical program.
Using the LINQ provider model, developers can extend LINQ to query other data sources besides those shown in Figure 3.1. The following are a few of the data sources currently enabled by third-party LINQ providers:
LINQ Extender |
LINQ to Google |
LINQ over C# project |
LINQ to Indexes |
LINQ to Active Directory |
LINQ to IQueryable |
LINQ to Amazon |
LINQ to JavaScript |
LINQ to Bindable Sources |
LINQ to JSON |
LINQ to CRM |
LINQ to LDAP |
LINQ to Excel |
LINQ to LLBLGen Pro |
LINQ to Expressions |
LINQ to Lucene |
LINQ to Flickr |
LINQ to Metaweb |
LINQ to Geo |
LINQ to MySQL |
LINQ to NCover |
LINQ to Sharepoint |
LINQ to NHibernate |
LINQ to SimpleDB |
LINQ to Opf3 |
LINQ to Streams |
LINQ to Parallel (PLINQ) |
LINQ to WebQueries |
LINQ to RDF Files |
LINQ to WMI |
These projects are of varying quality. Some, such as the LINQ Extender and LINQ to IQueryable, are merely tools for helping developers create providers. Nevertheless, you can see that an active community is interested in creating LINQ providers, and this community is producing some interesting products. By the time you read this, I’m sure the list of providers will be longer. See Appendix A for information on how to get updated information on existing providers.
One easily available provider called LinqToTerraServer can be found among the downloadable samples that ship with Visual Studio 2008. You can download the VS samples from the release tab found at http://code.msdn.microsoft.com/csharpsamples.
After unzipping the download, if you look in the ...\LinqSamples\WebServiceLinqProvider directory, you will find a sample called LinqToTerraServer. The TerraServer web site, http://terraserver-usa.com, is a vast repository of pictures and information about geographic information. The LinqToTerraServer example shows you how to create a LINQ provider that queries the web services provided on the TerraServer site. For example, the following query returns all U.S. cities and towns named Portland:
var query1 = from place in terraPlaces where place.Name == "Portland" select new { place.Name, place.State };
This query returns a number of locations, but here are a few of the more prominent:
{ Name = Portland, State = Indiana } { Name = Portland, State = Maine } { Name = Portland, State = Michigan } { Name = Portland, State = Oregon } { Name = Portland, State = Texas } { Name = Portland, State = Alabama } { Name = Portland, State = Arkansas } { Name = Portland, State = Colorado }
In Chapter 17, “LINQ Everywhere,” you will see examples of several other providers, including LINQ to Flickr and LINQ to SharePoint. It is not easy to create a provider.. After the code is written, however, it is easy to use the provider. In fact, you should already have enough familiarity with LINQ to see that it would be easy to modify the preceding query to suit your own purposes.
The LINQ provider model has hidden benefits that might not be evident at first glance:
- It is relatively open to examination and modification. As you read the next few chapters, you will find that most of the LINQ query pipeline is accessible to developers.
- It allows developers to be intelligent about how queries execute. You can get a surprising degree of control over the execution of a query. If you care about optimizing a query, in many cases you can optimize it, because you can see how it works.
- You can create a provider to publicize a data source that you have created. For instance, if you have a web service that you want C# developers to access, you can create a provider to give them a simple, extensible way to access your data.
I will return to the subject of LINQ providers later in the book. In this chapter, my goal is simply to make it clear that LINQ is extensible, and that its provider model is the basis on which each LINQ query model is built.
Query Operators
You don’t always need to use a LINQ provider to run queries against what might—at least at first—appear to be nontraditional data sources. By using the LINQ to Objects provider, and a set of built-in LINQ operators, you can run queries against a data source that does not look at all like XML or SQL data. For instance, LINQ to Objects gives you access to the reflection model that is built into C#.
The following query retrieves all the methods of the string class that are static:
var query = from m in typeof(string).GetMethods() where m.IsStatic == true select m;
The following are a few of the many results that this query returns:
System.String Join(System.String, System.String[]) System.String Join(System.String, System.String[], Int32, Int32) Boolean Equals(System.String, System.String) Boolean Equals(System.String, System.String, System.StringComparison) Boolean op_Equality(System.String, System.String) Boolean op_Inequality(System.String, System.String) Boolean IsNullOrEmpty(System.String) Int32 Compare(System.String, System.String) Int32 Compare(System.String, System.String, Boolean) Int32 Compare(System.String, System.String, System.StringComparison)
Using the power of LINQ, it is easy to drill into these methods to find out more about them. In particular, LINQ uses the extension methods mentioned in the preceding section to define a set of methods that can perform specific query operations such as ordering and grouping data. For instance, the following query retrieves the methods of the string class that are static, finds out how many overloads each method has, and then orders them first by the number of overloads and then alphabetically:
var query = from m in typeof(string).GetMethods() where m.IsStatic == true orderby m.Name group m by m.Name into g orderby g.Count() select new { Name = g.Key, Overloads = g.Count() }; foreach (var item in query) { Console.WriteLine(item); }
The results of this query look like this:
{ Overloads = 1, Name = Copy } { Overloads = 1, Name = Intern } { Overloads = 1, Name = IsInterned } { Overloads = 1, Name = IsNullOrEmpty } { Overloads = 1, Name = op_Equality } { Overloads = 1, Name = op_Inequality } { Overloads = 2, Name = CompareOrdinal } { Overloads = 2, Name = Equals } { Overloads = 2, Name = Join } { Overloads = 5, Name = Format } { Overloads = 9, Name = Concat } { Overloads = 10, Name = Compare }
This makes it obvious that Format, Compare, and Concat are the most frequently overloaded methods of the string class, and it presents all the methods with the same number of overloads in alphabetical order.
You can run this code in your own copy of Visual Studio because the LINQ to Objects provider ships with C# 3.0. Other third-party extensions to LINQ, such as LINQ to Amazon, are not included with Visual Studio. If you want to run a sample based on LINQ to Amazon or some other provider that does not ship with Visual Studio, you must download and install the provider before you can use it.