08 March 2009

Using LINQ to query SharePoint collections

I came across a challenge when I wanted to using LINQ on several SharePoint collection classes and I noticed that it wasn't possible to do this....So why not? LINQ lets you query any collection implementing the IEnumerable interface and after some research I found that most of the SharePoint collections don't implement this interface.

Ok, how to overcome this challenge? Arrays implement IEnumerable....Copy the items from the collection to an array! SharePoint collections implement the ICollection interface which provides the method: void CopyTo(Array array, int index), so this should not be a problem. Well it does!

SharePoint collections derive from a class called SPBaseCollection and this class implements ICollection. But because the explicit implementation of CopyTo, this method is private.

For this I wrote a little helper extension method that makes it possible to copy a SharePoint collections (or any class that implements ICollection) to an array of a particular type and validate for null at the same time.
public static class Helpers
{
 public static T[] CopyToArray(this ICollection collection)
 {
     if (collection == null) { return new T[0]; }

     T[] array = new T[collection.Count];
     collection.CopyTo(array, 0);

     return array;
 }
}
Now it is possible to make Linq queries like this:
using (SPSite site = new SPSite("http://demosite"))
{
 using (SPWeb web = site.OpenWeb())
 {
     // Get all webs ordered by Title
     var items1 = web.Webs.CopyToArray< SPWeb>().OrderBy(p => p.Title);
     foreach (var item in items1)
     {
         Console.WriteLine(item.Title);
     }

     // Get all content types grouped by group and ordered by group and name
     var items2 = web.ContentTypes.CopyToArray< SPContentType>().GroupBy(p => p.Group).OrderBy(g => g.Key);
     foreach (IGrouping<> group in items2)
     {
         Console.WriteLine(group.Key);

         foreach (var item in group.OrderBy(p => p.Name))
         {
             Console.WriteLine(item.Name);
         }
     }

     // Get a list by name and not by title
     var list = web.Lists.CopyToArray< SPList>().SingleOrDefault(p => p.RootFolder.Name.Equals("pages", StringComparison.InvariantCultureIgnoreCase));
     if (list != null)
     {
         Console.WriteLine("List: {0} found!", list.Title);
     }
 }
}

2 comments:

  1. "SharePoint collections derive from a class called SPBaseCollection and this class implements ICollection. But because the explicit implementation of CopyTo, this method is private."

    ICollection col = (ICollection)spCol;
    col.CopyTo(...);

    ReplyDelete
  2. @obiwanjacobi: That was the way I used it before. But beeing the lazy developer I am, I didn't want to validate for a null value and cast to ICollection every time I used a Linq query. So the Helper extension method CopyToArray was born. :-)

    ReplyDelete