Before we delve into the workings of
SPMetal, let’s spend some time looking at LINQ. We’ll examine what it
is, where it came from, and why it’s an essential tool for developing
applications using SharePoint 2010.
I have to confess that I’m a relatively late convert
to LINQ. I’ve been aware of the technology for some time but had always
assumed it to be some kind of framework for generating
Structured Query Language (SQL). As a developer who has spent many
years avoiding dynamically generating SQL statements in favor of
well-written and much more secure stored procedures, I’d always
considered the technology to be somewhat contradictory to established
best practice. What I’d failed to see was the power of LINQ outside the
SQL world.
LINQ is not just about SQL—fair enough, the syntax
is deliberately similar to SQL—but as a technology, it’s about working
with collections of data, not specifically relational database type
data, but in fact practically any collection of data that you’re ever
likely to use in the .NET world.
To illustrate the implications of such a tool, think
about the last application you wrote. How many for loops did you use to
locate specific items within collections of data? How many lines of
code did you write to handle situations in which the item you expected
wasn’t found within the collection? What about multiple collections
with related items? Did you use nested for loops to extract common data
into a new collection for use within your logic? If you’ve written any
application of more than a few lines long, you’ve likely used one or
more of these techniques.
The true power of LINQ is that it provides a much
more effective way to find and process exactly the data that you need.
No longer do you need to knock on every door in the street to find out
who lives at number 15; you can simply ask the question, “I’d like to
know the occupant name where the house number is 15,” and voila, the
magic that is LINQ will return the correct answer. But what if you live
in a town with many streets, each one with a “Number 15”? What if you
want to know who lives at number 15 Main Street specifically? You don’t
need to walk up and down every street knocking on every door; you can
simply ask the question, “I’d like to know the occupant name where the
house number is 15 and the street name is Main Street,” and, again,
LINQ will return the correct answer. This truly is powerful stuff. When
it comes to working with collections of data, LINQ is the tool we’ve
been waiting for.
Of course, LINQ isn’t really magic. There’s a
certain amount of smoke and mirrors involved, particularly with regard
to the SQL-like syntax. But behind the scenes it’s actually quite
simple. Let’s take a look at a few examples to illustrate how it works.
Locating Data Using an Iterator
One of the built-in implementations of LINQ is LINQ
to Objects, which is installed as part of the .NET Framework 3.5. Take
a look at this code snippet to get an idea of how it works:
List<string> members = new List<string> { "John", "Paul", "George", "Ringo" };
List<string> results = new List<string>();
foreach (string m in members)
{
if (m.Contains("n")) results.Add(m);
}
As you can see, this piece of code iterates through
a list of strings, returning a new list containing only those items
from the original list where the character n was found. Nothing groundbreaking here. However, if you include the System.Linq namespace in your class file, you’ll notice that the IntelliSense members’ list for the results object now includes a whole host of new methods. Interestingly, however, if you look up the documentation for a List<T>
object, you’ll find that none of the new methods are listed. There’s a
perfectly good explanation for this: these new methods are implemented
as extension methods, an essential
new feature in .NET 3.5 for supporting LINQ. Extension methods allow
developers to attach methods to a class by defining additional static
methods in a referenced assembly. Here’s an example:
public static class MyExtensions
{
public static string MyExtension(this List<string> list, string message)
{
return "MyExtension says " + message;
}
}
Notice the use of the this
modifier in the function signature. The modifier specifies the type to
which the extension methods should be attached. This example specifies
that the extension method should be available to objects of type List<string>. Extension methods work only when their containing namespace is explicitly imported—hence, the necessity to import the System.Linq
namespace to see the additional methods for this generic list. Strictly
speaking, the extension methods that we see are actually added to the
generic IEnumerable<TSource> interface and as such are available to any object that implements this interface.
Locating Data Using an Anonymous Delegate
One of the extension methods that we can make use of is the Where() method that we could use to rewrite our code as follows:
List<string> members = new List<string> { "John", "Paul", "George", "Ringo" };
var results = members.Where(delegate(string m) { return m.Contains("n"); });
The Where method
accepts an anonymous delegate as a parameter, and behind the scenes the
method is actually calling the delegate for every item in the list.
Whenever the delegate returns true, the item is added to a new results list. The results list is then returned when the Where
method has iterated through each item in the collection. From this
explanation, you can see that we’re actually performing much the same
work as our original function; we’re simply writing less code to do it.
Locating Data Using a Lambda Expression
We used an anonymous delegate in the preceding example, but .NET 3.5 introduces another new feature known as the lambda expression. Using a lambda in place of the anonymous delegate, we can rewrite our code as follows:
List<string> members = new List<string> { "John", "Paul", "George", "Ringo" };
var results = members.Where((string m) => { return m.Contains("n"); });
Lambda expressions make use of the => operator to separate the expression parameters from the expression body. This example defines an expression that accepts a string parameter named m.
You’ll notice that we don’t need to define the return type; just as
with an anonymous delegate, the compiler does this automatically for us.
Hopefully, you’ll see that using lambda expressions offer a more concise way of writing an anonymous method.
Locating Data Using LINQ
With more than a little sleight of hand and a
healthy dose of compiler voodoo, LINQ takes this expression syntax a
step further. Instead of hammering out several different styles of
brackets, we can simply rewrite our code as follows:
List<string> members = new List<string> { "John", "Paul", "George", "Ringo" };
var results = from m in members
where m.Contains("n")
select m;
As you’ve seen by working through these simple
examples, behind the scenes, LINQ to Objects is doing much the same
work that we would have done using an iterator; the new syntax simply
provides a much cleaner way of presenting the logic.
var changes = (from c in dxWrite.ChangeConflicts
from m in c.MemberConflicts
where m.CurrentValue.Contains(m.OriginalValue)
&& m.CurrentValue is string
select m).ToList();
I’m sure you can realize the benefit of the
LINQ syntax when compared to the complicated logic that you’d have to
implement to produce this result set using iterators.