LINQ to SharePoint and SPMetal
In previous versions of SharePoint, you could query lists and document libraries using Collaborative Application Markup Language (CAML). As an XML dialect, CAML syntax is relatively easy to pick up—you simply need to know which elements perform which functions, and then combine them to form a query. For example, a CAML query to return all items in a list named Products, where the product name begins with the letter A, might look something like this:
<Query>
<Where>
<BeginsWith>
<FieldRef Name='Title' />
<Value Type='Text'>A</Value>
</BeginsWith>
</Where>
</Query>
The CAML syntax covers a number of standard functions, including logic operations such as AND and OR as well as comparison operations such as greater than, less than, equal to, and between. The drawback of the CAML syntax, however, is that it’s not parsed by the compiler at compile time. Only when the statement is processed at runtime will an error be detected. To make matters worse, it’s often possible to parse an erroneous statement
and have the query return no results without returning an error. Take the following code snippet as an example:
SPList theList = SPContext.Current.Web.Lists["Products"];
SPQuery query=new SPQuery();
query.Query="<Query><Where><BeginsWith><FieldRef Name=\"Title\" />"+
"<Value Type=\"Text\">A</Value></BeginsWith></Where></Query>";
SPListItemCollection results = theList.GetItems(query);
At first glance, this query looks correct; however, the enclosing <Query> element is unnecessary. As a result, this query will not return any results. As you can imagine, debugging problems like this can be difficult and time consuming. One of the major drawbacks of the default SharePoint data access model is the use of indexed properties to store data. Since it isn’t possible for the compiler to determine whether an index value or key is valid at compile time, bugs often go undetected until runtime, when they are much more expensive to fix. Furthermore, even small changes to field names or identifiers can wreak havoc in an application, making refactoring a minefield. Since the introduction of SharePoint 2007, a number of solutions to this problem have been proposed. Ultimately, all such proposals have one common thread: the creation of a strongly typed data access layer. Index-based fields are wrapped with a strongly typed interface, which is in turn presented to the rest of the application as the single method of accessing data stored in indexed fields. Of course, this solution does not resolve the problem of a lack of compile time validation for indexed fields, but it does isolate runtime errors within a single layer, making resolving bugs much easier. So, instead of program logic that looks like this,
SPListItem myItem=properties.ListItem;
String myField=myItem["TestField"].ToString();
and we mistakenly enter this,
String myField=myItem["testField"].ToString();
we’ll get an object not found error, since the fields collection does not contain an item with the index testField.
We end up with something that looks like this:
MyDataObject d=new MyDataObject(properties.ListItem);
String myField=d.TestField;
With this code, a compilation error will occur if we mistype the field name. Furthermore, IntelliSense support will allow us to enter code much faster and more accurately. Of course, some overhead is incurred in implementing an additional data access layer. The programmer must create additional wrapper objects for each list, document library, or content type. In addition, the actual code involved in creating these objects is pretty mundane. As a result, a number of utilities have been written over the years that automatically generate data access objects from SharePoint lists and libraries. One such utility started life as SPMetal, part of a CodePlex project to deliver Language Integrated Query (LINQ) to SharePoint, created by Bart De Smet in 2007. Now, with SharePoint 2010, the SharePoint development team has picked up where Bart left off and have added complete LINQ functionality, including a new version of SPMetal as part of the standard toolset.
Overview of LINQ
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. However, consider this example, which we’ll revisit later in this:
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.
|