Using the SharePoint 2010 Client Object Model - Part 1
SharePoint 2010 introduces a new client side object model (hereafter referred to as the “client OM”) for retrieving data. It supports a subset of functionality contained in the server side object model, but generally speaking gives you ready access to the data in your SharePoint site using standard object model nomenclature. Before I actually get into the “codey” part of this posting, let me give a BIG SHOUT OUT to Shaofeng and Michael for giving me tips, tricks, pointers and a cheat sheet that were major sources of information as the code behind this six part blog posting was created. Thank you fellas!
The object model structure is very similar to what you've all known to expect, only with an interceding "Client" in the namespace. For example, in the server object model you have:
· Microsoft.SharePoint.SPSite
· Microsoft.SharePoint.SPWeb
· Microsoft.SharePoint.SPList
In the client object model you have:
· Microsoft.SharePoint.Client.Site
· Microsoft.SharePoint.Client.Web
· Microsoft.SharePoint.Client.List
You “get connected” to your data in SharePoint with the new ClientContext class. It is the uber class through which you issue queries and retrieve data from the farm. Where things get tricky is that when you return objects from a call, they have no data until you execute a query to retrieve them. The underlying goal here is to create a unified programming experience whether you are coding in SilverLight, ECMA (can I please just call it javascript the rest of the way??) and .NET managed applications. The client OM forces some of these semantics upon you, but in return it a) provides you data, b) minimizes the amount of data sent over the wire, and c) minimizes the number of roundtrips to the server. So how do these things work together?
Let’s start with a relatively simple example – getting a list of all the lists in your SharePoint site. First, I added a reference to Microsoft.SharePoint.Client.dll and Microsoft.SharePoint.Client.Runtime.dll. I then added this using statement at the top of my class:
using Microsoft.SharePoint.Client;
Now, to begin with we need to create our instance of the ClientContext class, which is going to form our connection:
PATTERN STEP 1: CREATE A CLIENT CONNECTION
ClientContext ctx = new ClientContext("https://foo");
Now we’ll start to use some of the built in properties of the ClientConnection class, starting with the Web for the current site (remember, it’s a “Web” and not “SPWeb” because we are using the client OM):
Web w = ctx.Web;
Now we have our object (w), but we still have no data. We’re going to tell the client OM that we want to populate the collection of lists. We use LINQ to do this, but there are a few different ways in which this can be accomplished; here is the simplest way.
PATTERN STEP 2: CREATE THE STATEMENT TO RETRIEVE THE DATA
var lists = ctx.LoadQuery(w.Lists);
Note that in the example above it will return a default set of properties. We could also pass in the list of properties that should be retrieved (we’ll look at that method in a bit). We still haven’t made a round trip to the server, we’ve just defined what data we want returned. To actually populate the collection of lists, we have to call the ExecuteQuery method on our ClientContext instance. Note that when you do call it though, LoadQuery affects ONLY the local variable named “lists”. It does not affect the ClientContext (specifically, w.Lists). In other words, after ExecuteQuery() is called the following will work:
foreach (List L in lists) {..}
the following will NOT work:
foreach (List L in w.Lists) {..}
Now, executing the query and getting our data back is our next line of code:
PATTERN STEP 3: EXECUTE THE QUERY
ctx.ExecuteQuery();
When we call ExecuteQuery, we actually send our request from the client to the server. The response is going to include all of the data we need, based on our LINQ query and the fields that we’ve asked for. The data comes back to us as simple formatted text that works well with JSON and REST. Here is snippet of the data that’s returned for each list when we asked for the default properties as shown above:
{
"_ObjectType_":"SP.List","_ObjectIdentity_":"740c6a0b-85e2-48a0-a494-e0f1759d4aa7:web:a496a84e-e865-4267-8bdc-b1b46df990de:list:ebcb8888-2892-440e-a80d-53c1c78b7339","_ObjectVersion_":"3","ParentWebUrl":"\u002f","HasExternalDataSource":false,
"Created":"\/Date(1256585338000)\/","LastItemModifiedDate":"\/Date(1256585451000)\/","LastItemDeletedDate":"\/Date(1256585338000)\/","Id":"\/Guid(ebcb8888-2892-440e-a80d-53c1c78b7339)\/","Description":"A test list","Title":"BigList","Direction":"none","BaseType":0,
"ImageUrl":"\u002f_layouts\u002fimages\u002fitdatash.png","ItemCount":3000,"BaseTemplate":120,
"DefaultContentApprovalWorkflowId":"\/Guid(00000000-0000-0000-0000-000000000000)\/","TemplateFeatureId":"\/Guid(00bfea71-3a1d-41d3-a0ee-651d11570120)\/","DefaultViewUrl":"\u002fLists\u002fBigList\u002fBig View.aspx","DefaultEditFormUrl":"\u002fLists\u002fBigList\u002fEditForm.aspx",
"DefaultNewFormUrl":"\u002fLists\u002fBigList\u002fNewForm.aspx",
"DefaultDisplayFormUrl":"\u002fLists\u002fBigList\u002fDispForm.aspx","EnableAttachments":true,
"ServerTemplateCanCreateFolders":true,"EnableFolderCreation":false,"EnableModeration":false,
"EnableVersioning":false,"ForceCheckout":false,"EnableMinorVersions":false,
"DraftVersionVisibility":0,"Hidden":false,"IsApplicationList":false,"IsCatalog":false,
"AllowContentTypes":true,"DocumentTemplateUrl":null,"ContentTypesEnabled":false,
"MultipleDataList":false,"NoCrawl":false
}
As I noted above, we can also create a query and just ask for specific fields. To do so we use the Load method on the ClientContext class and a somewhat more complicated LINQ query with Lambdas. Here is how that works:
ctx.Load(w.Lists, lists => lists.Include(prop => prop.Title,
prop => prop.Id, prop => prop.Hidden));
Basically what we’re saying here is that we want to retrieve items in the Lists property of the Web, we’re going to use a parameter called “lists”, and we’re going to define it as including the Title and Id fields.
We still call the ExecuteQuery after creating our LINQ statement, but here’s what the return data looks like for a single list with a specific list of fields:
{
"_ObjectType_":"SP.List","_ObjectIdentity_":"740c6a0b-85e2-48a0-a494-e0f1759d4aa7:web:a496a84e-e865-4267-8bdc-b1b46df990de:list:ebcb8888-2892-440e-a80d-53c1c78b7339","_ObjectVersion_":"3","Title":"BigList","Id":"\/Guid(ebcb8888-2892-440e-a80d-53c1c78b7339)\/","Hidden":false,"BaseType":0
}
The difference in size is impressive. In fact for this case I did a trace of both methods – asking for the collection of lists and all default fields resulted in a payload of 59k; calling the method with a list of just the three fields I needed resulted in a payload of 12k. This all adds up, especially when retrieving data over slow or congested connections.
PATTERN BEST PRACTICE: WHENEVER POSSIBLE, PROVIDE A SPECIFIC LIST OF FIELDS WHEN RETRIEVING DATA
So now that we’ve made that single round trip to the server, we won’t have to go back to it again in order to finish our example. With our data in hand, let’s complete the pattern to process the data in our results. In this case we’re going to enumerate all of the lists that were returned and add it to a list box in a Windows Forms application (NOTE: the example here is based on the code snippet above for retrieving specific fields; if you used the first example of retrieving all, you would enumerate through the var lists return value):
foreach (List theList in w.Lists)
{
if(theList.Hidden != true)
ListsLst.Items.Add(theList.Title);
}
One thing that’s important to remember is that when you retrieve items, if they have properties that are object collections, those values will not be retrieved when you query for the item. As an example, take the ListTemplates property of the Web class. It is a property of type ListTemplateCollection, filled with individual ListTemplate items. You won’t get the ListTemplateCollection property populated with the list of ListTemplates if you retrieve the Web object like this:
Web w = ctx.Web;
ctx.Load(w);
ctx.ExecuteQuery();
It will give you all of the simple property values of the Web object but that’s it. In order to populate those object collection properties, you have to pass them to the Load method like this:
ListTemplateCollection ltc = ctx.Web.ListTemplates;
ctx.Load(ltc);
ctx.ExecuteQuery();
//now enumerate foreach(ListTemplate lt in ltc){}
Coming Next…
That’s our basic pattern, and brings this posting to a close. In part 2 of this posting, I’ll describe how to use this pattern to retrieve data from a list.
Comments
Anonymous
January 01, 2003
The comment has been removedAnonymous
June 08, 2010
Hi All, I am new to SharePoint 2010 Development. I want to work on development, so what is the best way to learn the object modela and know what SP classes are there and how to utilize them. I am basically working on building custom claims provider for SP 2010 and People picker. Please guide me through this speschka Thanks BDSAnonymous
August 01, 2012
Nice post. Do you have a VS Solution for the examples?Anonymous
March 21, 2013
thanks - nice simple example. sharepoint is a complex leviathan in search of a reason to exist in my opinion. Any why does it not support ODMA? Very impractical from MS. No wonder they purchased Yammer ....Anonymous
April 24, 2014
ThanksAnonymous
September 18, 2014
The comment has been removed