Table of Contents
Using the Managed Client Object Model
How It Works
Creating a Windows Console Managed Client Object Model Application
The Managed Client Object Model
Object Identity
Trimming Result Sets
Creating and Populating a List
Using CAML to Query a List
Filtering the Child Collection returned by LoadQuery using LINQ
Using the LoadQuery Method
Increasing Performance by Nesting Includes in LoadQuery
Filtering the Child Collection returned by LoadQuery using LINQ
Updating Client Objects
Deleting Client Objects
Discovering the Schema for Fields
Accessing Large Lists
Asynchronous Processing
Other Resources
Introduction
A team leader has created a SharePoint team site with numerous lists that are necessary to manage her team’s mission. She has the need to do ad-hoc bulk modifications to these lists – perhaps updating assignments and estimates based on an Open XML spreadsheet, or moving items from one SharePoint list to another. She wants to write a small custom application to help her manage this. A software company that sells a traditional rich client application wants to integrate SharePoint document libraries and lists into their application, and they want this integration to be seamless, or even invisible to their users. A SharePoint developer wants to build a rich web part for SharePoint that brings list data into their custom Ajax web code. He also wants to build an even richer Silverlight application that does the same thing.
What do these people have in common? They can make use of the SharePoint Managed Client Object Model to accomplish their goals. The SharePoint Managed Client Object Model is a capability of SharePoint 2010 that allows us to write client-side code to work with all of the common objects in our SharePoint sites. Programs running on the client can add and remove lists, add, update, and delete list items, modify documents in document libraries, create sites, manage permissions of items, add and remove web parts from a page, and much more.
Prior to the availability of the Managed Client Object Model, these people didn’t have a lot of options. They could use the SharePoint Web Services, but this can be a fairly difficult task. If the SharePoint Web Services didn’t provide the capabilities that they needed, they could write server-side code to provide a new web service (an even more difficult task). Some IT departments disallow server-side code, or allow only code that is written by the IT department, so sometimes that isn’t even an option. The SharePoint Client Object Model enables new types of applications, and makes it much easier for developers to write client-side code that interacts with their SharePoint data.
Using the Managed Client Object Model
To use the Managed Client Object Model (sometimes referred to as the client object model), you write .NET managed code that uses an API that is similar to the SharePoint Foundation Server-Side Object Model that developers use on the server. The client object model has classes for accessing site collection information, site information, list and list item information, and much more.
In the case of web parts, you use an ECMAScript programming interface that is similar to the .NET API. For Silverlight, you use a subset of the API that is available through .NET on the client. While much of the information presented in this article is relevant to the ECMAScript and Silverlight APIs, this article will focus primarily on using the client object model from a .NET client application.
The client object model consists of two assemblies containing five namespaces. If you look at the classes available in those namespaces, you will see that there are very, very many of them. Don’t worry; many of those classes are used internally by the object model. We’re interested only in a subset of them, primarily classes that have direct counterparts to some familiar classes in the SharePoint Foundation Server-Side Object Model. The following table shows a few of the classes and their corresponding classes in the server object model:
|
Client
|
Server
|
|
Microsoft.SharePoint.Client.ClientContext
|
Microsoft.SharePoint.SPContext
|
|
Microsoft.SharePoint.Client.Site
|
Microsoft.SharePoint.SPSite
|
|
Microsoft.SharePoint.Client.Web
|
Microsoft.SharePoint.SPWeb
|
|
Microsoft.SharePoint.Client.List
|
Microsoft.SharePoint.SPList
|
|
Microsoft.SharePoint.Client.ListItem
|
Microsoft.SharePoint.SPListItem
|
|
Microsoft.SharePoint.Client.Field
|
Microsoft.SharePoint.SPField
|
You’ll notice that the client object model uses the same legacy naming pattern for site collections and sites as the server object model. The Site class represents site collections, and the Web class represents sites. My preference for using these classes is to name the variables so that the variable name indicates whether it is a site collection or a site, even though we must use the Site and Web classes to declare them:
ClientContext clientContext = new ClientContext(siteUrl);
Site siteCollection = clientContext.Site;
Web site = clientContext.Web;
How It Works
An application that uses SharePoint data will interact with the API in a number of ways – call methods and get the return values, pass a CAML query and get the results, and set/get properties. After you use the API to accomplish a specific task the client object model bundles up all of these uses of the API into some XML that it sends to the SharePoint server. The server receives this request, and makes appropriate calls into the object model on the server, collects the responses, and forms them into Java Script Object Notation (JSON), and sends that JSON back to the client object model. The client object model parses the JSON and presents the results to the application as .NET objects (or JavaScript objects in the case of ECMAScript). The following diagram shows these interactions:
It is important to note that you control when the client object model initiates the process of sending the XML to the server and receiving the JSON back from the server. We’ll see how shortly.
The bundling of multiple method calls into a single round trip to the server are dictated by the realities of network speed, network latency, and desired performance characteristics. If the client object model sent data to the server with every method request, the performance of the system, and the increased network traffic would make the system unworkable.
As I mentioned, we must explicitly control when the client object model bundles method calls and sends a request to the server. As part of this process, before initiating the round-trip to the server, we must explicitly specify what data we want to retrieve from the server. This is the biggest difference between the client object model and the server object model. But once you understand the model, it is pretty easy. The easiest way to start understanding the difference is to see a simple application.
Creating a Windows Console Managed Client Object Model Application
This article uses Windows console applications for the sample code, but you can use the same approach with other application types.
To build the application, you need to add references to two assemblies, Microsoft.SharePoint.Client.dll and Microsoft.SharePoint.Client.Runtime.dll. Installing SharePoint installs these assemblies on the SharePoint server. The two assemblies are located at:
%ProgramFiles%\Common Files\Microsoft Shared\web server extensions\14\ISAPI
Note: For the pre-release versions of SharePoint 2010 Foundation Server and SharePoint 2010 Server, you must install the assembles as described here. After the release, the procedure for installing the SharePoint 2010 Managed Client Object Model assemblies will change. I will update this post and the MSDN article with the new procedures around the time of the final release of SharePoint Server 2010.
Copy those two assemblies and place them in a convenient location on your development client computer. You will need to browse to those assemblies to add references to them when you are setting up projects that use the client object model.
To Build the Application
Start Microsoft Visual Studio 2010.
On the File menu, point to New, and then click Project.
In the New Project dialog box, in the Recent Template pane, expand Visual C#, and click Windows.
To the right of the Recent Template pane, Click Console Application.
By default, Visual Studio creates a project that targets the .NET Framework 4, but we must target the .NET Framework 3.5. From the list at the upper part of the File Open Dialog, Select .NET Framework 3.5.
In the Name box, type a name for your project, such as FirstClientApiApplication.
In the Location box, type the location where you want to place the project.
Click OK to create the solution
Add References to the Microsoft.SharePoint.Client and Microsoft.SharePoint.Client.Runtime Assemblies
The classes that you use in a client object model application are located in Microsoft.SharePoint.Client.dll and Microsoft.SharePoint.Client.Runtime.dll. As I mentioned, before adding those references, you need to copy those assemblies from the SharePoint 2010 Server to your client development computer.
On the Project menu, click Add Reference to open the Add Reference Dialog box.
Select the Browse tab, navigate to the location where you placed Microsoft.SharePoint.Client.dll and Microsoft.SharePoint.Client.Runtime.dll, select both DLLs, and click OK.
Add the Sample Code to the Solution
In Visual Studio, replace the contents of the Program.cs source file with the following code:
using System;
using Microsoft.SharePoint.Client;
class DisplayWebTitle
{
static void Main()
{
ClientContext clientContext =
new ClientContext("http://intranet.contoso.com");
Web site = clientContext.Web;
clientContext.Load(site);
clientContext.ExecuteQuery();
Console.WriteLine("Title: {0}", site.Title);
}
}
[Download Code]
Replace the URL in the ClientContext constructor with the URL to your SharePoint site. Build and run the solution. The example prints the title of the site.
Just as with the SharePoint Foundation Server-Side Object Model, you create a context for the SharePoint site that you want to access. You can then retrieve a reference to the site from the context.
The call to ClientContext.ExecuteQuery causes the client object model to send the request to the server. There will not be any network traffic until the application calls ClientContext.ExecuteQuery.
An important point to make about this example is that the call to the Load method doesn’t actually load anything. Instead, it informs the client object model that when the application calls the ClientContext.ExecuteQuery method, you want to load the property values of the siteCollection object.
This is the model that all interactions with the server take:
-
You inform the client object model about the operations that you want to take. This includes accessing the values of properties of objects (for example, objects of the List, ListItem, and Web classes), CAML queries that you want to run, and objects such as ListItem objects that you want to insert, update or delete.
-
You then call ClientContext.ExecuteQuery. No network traffic takes place until you call ExecuteQuery. Until that point, your application is only registering its requests.
Now that you have seen a simple example that demonstrates the nature of client object model applications, i.e. you first set up a query, then you execute the query, which causes the client object model to send traffic on a round-trip to the server, let’s take a detailed look at the model, why it is designed the way it is, and how you code applications using the model.
The Managed Client Object Model
There are a number of aspects of the client object model that we must examine. There are some specific approaches that the client object model takes to minimize network traffic. There are powerful ways that we can construct queries. There are techniques that we can use to increase performance on the server. We need to see how to create, update, and delete client objects. There are approaches that we can take to work with very large lists. But before we dive into any of these subjects, we need to examine the issue of object identity.
Object Identity
The key idea behind object identity is that client objects refer to their corresponding SharePoint object both before and after calling ExecuteQuery, and they continue to refer to that same SharePoint object through multiple calls to ExecuteQuery.
This means that in the process of setting up the query, the client object model will return objects to us that we can use further in setting up the query before calling ExecuteQuery. This allows us to write more complex queries before starting the round-trip to the server. We can do more interesting things in a single query, and eliminate network traffic.
The following example gets the Announcements list object, and then gets all items of that list (using the simplest possible CAML query).
using System;
using Microsoft.SharePoint.Client;
class Program
{
static void Main()
{
ClientContext clientContext =
new ClientContext("http://intranet.contoso.com");
List list = clientContext.Web.Lists.GetByTitle("Announcements");
CamlQuery camlQuery = new CamlQuery();
camlQuery.ViewXml = "<View/>";
ListItemCollection listItems = list.GetItems(camlQuery);
clientContext.Load(list);
clientContext.Load(listItems);
clientContext.ExecuteQuery();
foreach (ListItem listItem in listItems)
Console.WriteLine("Id: {0} Title: {1}", listItem.Id,
listItem["Title"]);
}
}
[Download Code]
Notice the sequence in this example:
-
It first gets a Microsoft.SharePoint.Client.List object using the ListCollection.GetByTitle method. Remember, this List object has no data in it; it won’t have data in any of its properties until the application calls the ExecuteQuery method.
-
It then calls the GetItems method on the list object, even though that list object hasn’t been populated with data.
-
It finally calls the Load method on both the list and listItems objects, and then calls the ExecuteQuery method.
The key point about this is that the client object model remembers that the list object is the one that the application initialized using the ListCollection.GetByTitle method, and that the client object model should execute the CAML query on that same list object after the list object has been retrieved from the SharePoint database. Any class that derives from Microsoft.SharePoint.Client.ClientObject has these semantics.
And, as mentioned, we can continue to use client objects to setup further queries after calling the ExecuteQuery method. In the following example, the code loads the list and calls the ExecuteQuery method. It then uses that list client object to call the List.GetItems method, and then calls ExecuteQuery again. The list object retained its identity through the call to ExecuteQuery.
using System;
using Microsoft.SharePoint.Client;
class Program
{
static void Main()
{
ClientContext clientContext =
new ClientContext("http://intranet.contoso.com");
List list = clientContext.Web.Lists
.GetByTitle("Announcements");
clientContext.Load(list);
clientContext.ExecuteQuery();
Console.WriteLine("List Title: {0}", list.Title);
CamlQuery camlQuery = new CamlQuery();
camlQuery.ViewXml = "<View/>";
ListItemCollection listItems = list.GetItems(camlQuery);
clientContext.Load(listItems);
clientContext.ExecuteQuery();
foreach (ListItem listItem in listItems)
Console.WriteLine("Id: {0} Title: {1}",
listItem.Id, listItem["Title"]);
}
}
[Download Code]
Some properties and methods return objects or value types that do not derive from the Microsoft.SharePoint.Client.ClientObject class. We benefit from using client object identity to access methods and properties only when those methods and properties return client objects or collections of them. For instance, some classes, such as FieldUrlValue and FieldLookupValue derive from the ClientValueObject class, and we can’t make use of properties that return those types until after the call to the ExecuteQuery method. Some properties return .NET types such as string or integer, and we also can’t use properties or methods that return those until after the call to the ExecuteQuery method. Since we can’t use the values of any properties until those values have been populated in the ExecuteQuery call, we can’t, for instance, find an item in a list, and use the value of one of that item’s fields to select items in a further query. If we try to use a property before it has been populated by the ExecuteQuery method, the client object model will throw a PropertyOrFieldNotInitializedException.
Important note: Client object identity is valid only for a single ClientContext object. If we initialize another ClientContext object to the same SharePoint site, we can’t use client objects from one client context with the other one.
We’ll make use of object identity behavior in a number of the examples that I present in this article.
This example doesn’t do any error handling. If the Announcements list doesn’t exist, the client object model will throw an exception in the call to the ExecuteQuery method. You should be prepared to catch exceptions when you write code that may fail if you ask for objects that may not exist.
Trimming Result Sets
SharePoint 2010 Server is often deployed in organizations with many thousands of users. When building an application that accesses SharePoint 2010 Server across the network, it makes sense to build it so that that it uses the least amount of network traffic. There are a number of ways that the client object model helps us do this. The simplest approach is to use lambda expressions to specify exactly which properties the client object model should return to the application.
The following example shows how to specify that when the client object model loads the site object, it needs to load only the Title and Description properties. This will reduce the size of the JSON response from the server back to the client object model.
using System;
using Microsoft.SharePoint.Client;
class Program
{
static void Main()
{
ClientContext clientContext =
new ClientContext("http://intranet.contoso.com");
Web site = clientContext.Web;
clientContext.Load(site,
s => s.Title,
s => s.Description);
clientContext.ExecuteQuery();
Console.WriteLine("Title: {0} Description: {1}",
site.Title, site.Description);
}
}
[Download Code]
If we don’t include these lambda expressions in the call to the Load method, then by default it loads a much larger number of properties (but not all of them). The first two examples called Load without specifying which properties to load, so the JSON packet that the server returned was somewhat larger than it needed to be. While in these small examples, it doesn’t make much difference, when loading thousands of list items, carefully specifying the required properties will reduce network traffic.
This use of lambda expressions provides a means for us to specify a list of our desired .NET properties to the Load method. But reducing network traffic isn’t the only benefit we derive from the client object model’s use of lambda expressions. Later on in this article, we’ll see how we can filter result sets using lambda expressions.
Next I'll show an example that creates a list and add some data to it. Then we’ll have some sample data to work with for the rest of this article.
Creating and Populating a List
The following example creates a list, and adds a couple of fields and a few items to it.
using System;
using Microsoft.SharePoint.Client;
class Program
{
static void Main()
{
ClientContext clientContext =
new ClientContext("http://intranet.contoso.com");
Web site = clientContext.Web;
// Create a new list.
ListCreationInformation listCreationInfo =
new ListCreationInformation();
listCreationInfo.Title = "Client API Test List";
listCreationInfo.TemplateType = (int)ListTemplateType.GenericList;
List list = site.Lists.Add(listCreationInfo);
// Add fields to the list.
Field field1 = list.Fields.AddFieldAsXml(
@"<Field Type='Choice'
DisplayName='Category'
Format='Dropdown'>
<Default>Specification</Default>
<CHOICES>
<CHOICE>Specification</CHOICE>
<CHOICE>Development</CHOICE>
<CHOICE>Test</CHOICE>
<CHOICE>Documentation</CHOICE>
</CHOICES>
</Field>",
true, AddFieldOptions.DefaultValue);
Field field2 = list.Fields.AddFieldAsXml(
@"<Field Type='Number'
DisplayName='Estimate'/>",
true, AddFieldOptions.DefaultValue);
// Add some data.
ListItemCreationInformation itemCreateInfo =
new ListItemCreationInformation();
ListItem listItem = list.AddItem(itemCreateInfo);
listItem["Title"] = "Write specs for user interface.";
listItem["Category"] = "Specification";
listItem["Estimate"] = "20";
listItem.Update();
listItem = list.AddItem(itemCreateInfo);
listItem["Title"] = "Develop proof-of-concept.";
listItem["Category"] = "Development";
listItem["Estimate"] = "42";
listItem.Update();
listItem = list.AddItem(itemCreateInfo);
listItem["Title"] = "Write test plan for user interface.";
listItem["Category"] = "Test";
listItem["Estimate"] = "16";
listItem.Update();
listItem = list.AddItem(itemCreateInfo);
listItem["Title"] = "Validate SharePoint interaction.";
listItem["Category"] = "Test";
listItem["Estimate"] = "18";
listItem.Update();
listItem = list.AddItem(itemCreateInfo);
listItem["Title"] = "Develop user interface.";
listItem["Category"] = "Development";
listItem["Estimate"] = "18";
listItem.Update();
clientContext.ExecuteQuery();
}
}
[Download Code]
In many cases, where it is possible to create a client object, the application can call an Add method that takes as an argument an object that specifies creation information. In this example, you can see the use of the ListCreationInformation class for creating a List object, and the use of the ListItemCreationInformation class for creating a ListItem object. You often will set properties of the creation information class after instantiating it. You can see that the code sets the Title and TemplateType properties of the ListItemCreationInformation object. Note that to create a list, you call the ListCollection.Add method, but to create a ListItem, you call the List.AddItem method. One is on the collection, and the other is on the singleton.
Creating fields in a list also doesn’t use a method named Add that takes a FieldCreationInformation object as an argument, because when we create fields, we are not really creating an instance of the Microsoft.SharePoint.Client.Field class; we are creating an instance of a class that derives from Field. There are many, many options available for those derived classes, and this would significantly complicate the design of a FieldCreationInformation class; for this reason, the client object model doesn’t include such a class. Instead, the simplest way to create a field is to specify a little bit of XML that defines the field, and pass that XML to the FieldCollection.AddFieldAsXml method. There is a FieldCollection.Add method that we can use to create a field, but instead of taking a FieldCreationInformation object, it takes another Field object as a parameter that it uses as a prototype for the field to be created. This is useful in some scenarios.
In the Discovering the Schema for Fields
section of this article, I’ll show you an easy way to discover the XML that you need to specify for fields that you want to create.
It’s important to note that, of course, no objects are actually added to the SharePoint database until the application calls ExecuteQuery.
There is one more item of interest in this example. Notice that after calling the List.AddItem method, the example sets three indexed properties. We are setting the values of the fields that were just added to the list. After setting these properties, the application must call the ListItem.Update method, informing the client object model that those objects have been modified. The client object model will not work properly if you don’t do so. We will see the use of the Update method in further examples, when I show how to modify existing client objects.
Now that we have some data, let’s discover some interesting ways to query and modify it.
Using CAML to Query a List
The following example shows how to query the list that we created in the last example using CAML. This example prints the Development items from our test list.
using System;
using Microsoft.SharePoint.Client;
class Program
{
static void Main(string[] args)
{
ClientContext clientContext =
new ClientContext("http://intranet.contoso.com");
List list = clientContext.Web.Lists
.GetByTitle("Client API Test List");
CamlQuery camlQuery = new CamlQuery();
camlQuery.ViewXml =
@"<View>
<Query>
<Where>
<Eq>
<FieldRef Name='Category'/>
<Value Type='Text'>Development</Value>
</Eq>
</Where>
</Query>
<RowLimit>100</RowLimit>
</View>";
ListItemCollection listItems = list.GetItems(camlQuery);
clientContext.Load(
listItems,
items => items
.Include(
item => item["Title"],
item => item["Category"],
item => item["Estimate"]));
clientContext.ExecuteQuery();
foreach (ListItem listItem in listItems)
{
Console.WriteLine("Title: {0}", listItem["Title"]);
Console.WriteLine("Category: {0}", listItem["Category"]);
Console.WriteLine("Estimate: {0}", listItem["Estimate"]);
Console.WriteLine();
}
}
}
[Download Code]
This example produces the following output:
Title: Develop proof-of-concept.
Category: Development
Estimate: 42
Title: Develop user interface.
Category: Development
Estimate: 18
You may notice that there is a difference between the lambda expressions that we specify in this example and the lambda expressions in the example presented in the Trimming Result Sets
section. We must use the ClientObjectQueryableExtension.Include extension method to specify the properties that we want to load for each item in the collection that we’re loading. The items parameter of the lambda expression is of type ListItemCollection, which of course doesn’t contain an indexed property that allows us to specify which properties to load for items in the collection. Instead, we call the Include extension method, which allows us to specify which parameters of that child collection to load. Parameters to lambda expressions in the Include extension method are of the type of the items of the collection, so this allows us to specify the properties that we want to load for each item in the collection.
Again, it isn’t entirely necessary to understand the exact semantics of this use of lambda expressions. Just remember two coding idioms:
If you are requesting that the client object model load certain properties of a client object (not a collection of them), then specify the properties in the lambda expression that you place directly in the Load method:
clientContext.Load(site,
s => s.Title,
s => s.Description);
If you are requesting that the client object model load certain properties of each of the items in a collection of client objects, then use the Include extension method, and pass the lambda expressions that specify your desired properties to the Include method:
clientContext.Load(
listItems,
items => items
.Include(
item => item["Title"],
item => item["Category"],
item => item["Estimate"]));
Note: I'm currently working on a blog post on exactly how lambda expressions work in the client object model. Now THAT is going to be a geeky post! J
Filtering the Child Collection returned by Load using LINQ
Because the ClientObjectQueryableExtension.Include extension method returns IQueryable<T>, we can chain from the Include method into the IQueryable<T>.Where extension method. This provides a succinct way to filter the result set. We should only use this capability when querying client object collections other than collections of ListItem objects, because while we could use this technique to filter collections of ListItem objects, the use of CAML will result in better performance. This is important enough to say again:
Never use the IQueryable<T>.Where extension method when querying ListItem objects. The reason is that under the covers, the client object model first evaluates the result of the CAML query, retrieves the results, and then filters the resulting collection using LINQ. If you filter an extremely large list using LINQ instead of CAML, behind the scenes, the client object model will attempt to retrieve all items in the list before filtering with CAML and will either be issuing queries that will take too much in the way of system resources, or perhaps will not succeed, and the reason won't be evident unless you know how the client object model works internally. So we need to continue to use CAML when querying list items. There are some interesting ways that we can make CAML easier to use. But that's another blog post.
The following example queries the client object model for all lists that are not hidden. Notice that we must include a using directive for System.Linq.
using System;
using System.Linq;
using Microsoft.SharePoint.Client;
class Program
{
static void Main(string[] args)
{
ClientContext clientContext =
new ClientContext("http://intranet.contoso.com");
ListCollection listCollection = clientContext.Web.Lists;
clientContext.Load(
listCollection,
lists => lists
.Include(
list => list.Title,
list => list.Hidden)
.Where(list => ! list.Hidden)
);
clientContext.ExecuteQuery();
foreach (var list in listCollection)
Console.WriteLine(list.Title);
}
}
[Download Code]
On my computer, this example produces the following output:
Announcements
Calendar
Client API Test List
Content and Structure Reports
Customized Reports
Eric's ToDo List
Eric's Wiki
Form Templates
Links
Reusable Content
Shared Documents
Site Assets
Site Collection Documents
Site Collection Images
Site Pages
Style Library
Tasks
Team Discussion
Workflow Tasks
Using the LoadQuery Method
The LoadQuery method is similar in functionality to the Load method, except that in certain circumstances, the client object model can process the queries more efficiently and use memory more efficiently. And it also allows for a more flexible programming style.
LoadQuery has different semantics than the Load method. Whereas the Load method populates the client object (or client object collection) with data from the server, the LoadQuery method populates and returns an entirely new collection. This means that you can query the same object collection multiple times and retain separate result sets for each query. For instance, you can query for all items in a project list that are assigned to a certain person, and separately query for all items that have an estimated hours that is greater than a certain threshold, and access both result sets simultaneously. It also allows you to let these collections go out of scope, and thereby become eligible for garbage collection. Collections that you load using the Load method can be garbage collected only when the client context variable itself goes out of scope. Other than these distinctions, the LoadQuery method is very similar to the Load method.
The following example uses LoadQuery to retrieve a list of all the lists in the site.
using System;
using System.Collections.Generic;
using Microsoft.SharePoint.Client;
class Program
{
static void Main(string[] args)
{
ClientContext clientContext =
new ClientContext("http://intranet.contoso.com");
Web site = clientContext.Web;
ListCollection lists = site.Lists;
IEnumerable<List> newListCollection = clientContext.LoadQuery(
lists.Include(
list => list.Title,
list => list.Id,
list => list.Hidden));
clientContext.ExecuteQuery();
foreach (List list in newListCollection)
Console.WriteLine("Title: {0} Id: {1}",
list.Title.PadRight(40), list.Id.ToString("D"));
}
}
[Download Code]
Notice that the LoadQuery method returns an entirely new list collection that we can iterate through. The new list collection has a type of IEnumerable<List> instead of ListCollection.
There is one aspect of the semantics of LoadQuery that you need to pay attention to. In the above example, the original lists variable doesn’t have its property values populated after ExecuteQuery returns. If you want that list to be populated, you must explicitly call Load on it, specifying which properties you want loaded.
Increasing Performance by Nesting Includes in LoadQuery
When calling the LoadQuery method, we can specify multiple levels of properties to load. This allows the client object model to optimize its access to the SharePoint server internally by reducing the number of times the client object model must do a round-trip to the SharePoint server to retrieve the data that you need. The following query retrieves all lists from the site, and all fields from each list. It then prints them to the console, indicating if each list or field is hidden.
using System;
using System.Collections.Generic;
using Microsoft.SharePoint.Client;
class Program
{
static void Main()
{
ClientContext clientContext =
new ClientContext("http://intranet.contoso.com");
IEnumerable<List> lists = clientContext.LoadQuery(
clientContext.Web.Lists.Include(
list => list.Title,
list => list.Hidden,
list => list.Fields.Include(
field => field.Title,
field => field.Hidden)));
clientContext.ExecuteQuery();
foreach (List list in lists)
{
Console.WriteLine("{0}List: {1}",
list.Hidden ? "Hidden " : "", list.Title);
foreach (Field field in list.Fields)
Console.WriteLine(" {0}Field: {1}",
field.Hidden ? "Hidden " : "",
field.Title);
}
}
}
[Download Code]
This approach allows the server portion of the client object model to be more efficient than if the application first loaded a list of lists, and then loaded fields for each list.
Filtering the Child Collection returned by LoadQuery using LINQ
The LoadQuery method takes an object of type IQueryable<T> as its parameter, and this allows us to write LINQ queries instead of CAML to filter the results. This example returns a collection of all document libraries that are not hidden.
using System;
using System.Linq;
using System.Collections.Generic;
using Microsoft.SharePoint.Client;
class Program
{
static void Main(string[] args)
{
ClientContext clientContext =
new ClientContext("http://intranet.contoso.com");
ListCollection listCollection = clientContext.Web.Lists;
IEnumerable<List> hiddenLists = clientContext.LoadQuery(
listCollection
.Where(list => !list.Hidden &&
list.BaseType == BaseType.DocumentLibrary));
clientContext.ExecuteQuery();
foreach (var list in hiddenLists)
Console.WriteLine(list.Title);
}
}
[Download Code]
Updating Client Objects
Updating client objects using the client object model is pretty simple. We retrieve the object(s), alter properties, call the Update method for each object we change, and then call the ExecuteQuery method. The following example modifies items in the Client API Test List, increasing the estimate for all development items by 50% (a common operation):
using System;
using Microsoft.SharePoint.Client;
class Program
{
static void Main(string[] args)
{
ClientContext clientContext =
new ClientContext("http://intranet.contoso.com");
List list = clientContext.Web.Lists
.GetByTitle("Client API Test List");
CamlQuery camlQuery = new CamlQuery();
camlQuery.ViewXml =
@"<View>
<Query>
<Where>
<Eq>
<FieldRef Name='Category'/>
<Value Type='Text'>Development</Value>
</Eq>
</Where>
</Query>
<RowLimit>100</RowLimit>
</View>";
ListItemCollection listItems = list.GetItems(camlQuery);
clientContext.Load(
listItems,
items => items.Include(
item => item["Category"],
item => item["Estimate"]));
clientContext.ExecuteQuery();
foreach (ListItem listItem in listItems)
{
listItem["Estimate"] = (double)listItem["Estimate"] * 1.5;
listItem.Update();
}
clientContext.ExecuteQuery();
}
}
[Download Code]
Deleting Client Objects
Deleting client objects is just as easy. However, there is one very important dynamic around deleting client objects from a client object collection. We can’t simply iterate through the collection, deleting objects. As soon as we delete the first object, it causes the iterator of the client object collection to malfunction. The iterator may throw an exception, or it may quietly finish but not visit all items in the collection. Instead, we need to materialize the collection into a List<T> using the ToList method, and then iterate through that list, deleting the client objects.
The following example deletes the test items from our Client API Test List. It shows using the ToList method to materialize the collection before we iterate through it:
using System;
using System.Linq;
using System.Collections.Generic;
using Microsoft.SharePoint.Client;
class Program
{
static void Main(string[] args)
{
ClientContext clientContext = new ClientContext("http://intranet.contoso.com");
List list = clientContext.Web.Lists.GetByTitle("Client API Test List");
CamlQuery camlQuery = new CamlQuery();
camlQuery.ViewXml =
@"<View>
<Query>
<Where>
<Eq>
<FieldRef Name='Category'/>
<Value Type='Text'>Test</Value>
</Eq>
</Where>
</Query>
<RowLimit>100</RowLimit>
</View>";
ListItemCollection listItems = list.GetItems(camlQuery);
clientContext.Load(
listItems,
items => items.Include(
item => item["Title"]));
clientContext.ExecuteQuery();
foreach (ListItem listItem in listItems.ToList())
listItem.DeleteObject();
clientContext.ExecuteQuery();
}
}
[Download Code]
The following code snippet shows the incorrect approach:
clientContext.Load(
listItems,
items => items.Include(
item => item["Title"]));
clientContext.ExecuteQuery();
// the following line doesn’t include the call to ToList
foreach (ListItem listItem in listItems)
listItem.DeleteObject();
clientContext.ExecuteQuery();
Finally, just to clean up the Client API Test List, here is an example that deletes the list and its items:
using System;
using Microsoft.SharePoint.Client;
class DisplayWebTitle
{
static void Main()
{
ClientContext clientContext =
new ClientContext("http://intranet.contoso.com");
clientContext.Web.Lists.GetByTitle("Client API Test List")
.DeleteObject();
clientContext.ExecuteQuery();
}
}
[Download Code]
Discovering the Schema for Fields
As promised, this section shows an easy way to discover the XML schema that you need to know to create the fields that you want to create in a list. First, go into your SharePoint site, and create a list that contains columns that are configured as you want them. Then you can then use the small example below to output the XML that will create those fields.
The following example prints the field schemas for the fields that I added to the Client API Test List:
using System;
using System.Linq;
using System.Xml.Linq;
using Microsoft.SharePoint.Client;
class Program
{
static void Main(string[] args)
{
ClientContext clientContext =
new ClientContext("http://intranet.contoso.com");
List list = clientContext.Web.Lists
.GetByTitle("Client API Test List");
clientContext.Load(list);
FieldCollection fields = list.Fields;
clientContext.Load(fields);
clientContext.ExecuteQuery();
foreach (var f in fields)
{
XElement e = XElement.Parse(f.SchemaXml);
string name = (string)e.Attribute("Name");
if (name == "Category" || name == "Estimate")
{
e.Attributes("ID").Remove();
e.Attributes("SourceID").Remove();
e.Attributes("ColName").Remove();
e.Attributes("RowOrdinal").Remove();
e.Attributes("StaticName").Remove();
Console.WriteLine(e);
Console.WriteLine("===============");
}
}
}
}
[Download Code]
When you run this after creating the list using the example program in the section Creating and Populating a List
, it produces the following output:
<Field Type="Choice" DisplayName="Category" Format="Dropdown" Name="Category">
<Default>Specification</Default>
<CHOICES>
<CHOICE>Specification</CHOICE>
<CHOICE>Development</CHOICE>
<CHOICE>Test</CHOICE>
<CHOICE>Documentation</CHOICE>
</CHOICES>
</Field>
===============
<Field Type="Number" DisplayName="Estimate" Name="Estimate" />
===============
The example removes attributes that you don’t need for creating the field.
Accessing Large Lists
SharePoint development guidelines indicate that you should not attempt to retrieve more than 2000 items in a single query. If this is a possibility in your application, consider using the RowLimit element in your CAML queries to limit the amount of data that the client object model retrieves for your application. Sometimes you must access all items in a list that may contain more than 2000 items. If you must do so, then best practice is to page through the items 2000 at a time. This section presents an approach to paging using the CamlQuery.ListItemCollectionPosition property.
using System;
using System.Linq;
using Microsoft.SharePoint.Client;
class Program
{
static void Main()
{
ClientContext clientContext =
new ClientContext("http://intranet.contoso.com");
List list = clientContext.Web.Lists
.GetByTitle("Client API Test List");
// First, add 20 items to Client API Test List so that there are
// enough records to show paging.
ListItemCreationInformation itemCreateInfo =
new ListItemCreationInformation();
for (int i = 0; i < 20; i++)
{
ListItem listItem = list.AddItem(itemCreateInfo);
listItem["Title"] = String.Format("New Item #{0}", i);
listItem["Category"] = "Development";
listItem["Estimate"] = i;
listItem.Update();
}
clientContext.ExecuteQuery();
// This example shows paging through the list ten items at a time.
// In a real-world scenario, you would want to limit a page to
// 2000 items.
ListItemCollectionPosition itemPosition = null;
while (true)
{
CamlQuery camlQuery = new CamlQuery();
camlQuery.ListItemCollectionPosition = itemPosition;
camlQuery.ViewXml =
@"<View>
<ViewFields>
<FieldRef Name='Title'/>
<FieldRef Name='Category'/>
<FieldRef Name='Estimate'/>
</ViewFields>
<RowLimit>10</RowLimit>
</View>";
ListItemCollection listItems = list.GetItems(camlQuery);
clientContext.Load(listItems);
clientContext.ExecuteQuery();
itemPosition = listItems.ListItemCollectionPosition;
foreach (ListItem listItem in listItems)
Console.WriteLine(" Item Title: {0}", listItem["Title"]);
if (itemPosition == null)
break;
Console.WriteLine(itemPosition.PagingInfo);
Console.WriteLine();
}
}
}
[Download Code]
This example produces the following output:
Item Title: Write specs for user interface.
Item Title: Develop proof-of-concept.
Item Title: Write test plan for user interface.
Item Title: Validate SharePoint interaction.
Item Title: Develop user interface.
Item Title: New Item #0
Item Title: New Item #1
Item Title: New Item #2
Item Title: New Item #3
Item Title: New Item #4
Paged=TRUE&p_ID=10
Item Title: New Item #5
Item Title: New Item #6
Item Title: New Item #7
Item Title: New Item #8
Item Title: New Item #9
Item Title: New Item #10
Item Title: New Item #11
Item Title: New Item #12
Item Title: New Item #13
Item Title: New Item #14
Paged=TRUE&p_ID=20
Item Title: New Item #15
Item Title: New Item #16
Item Title: New Item #17
Item Title: New Item #18
Item Title: New Item #19
Asynchronous Processing
If you are building an application that needs to attach to SharePoint sites that may or may not be available, or if you need to regularly invoke queries that may take a long time, you should consider using asynchronous processing. This will allow you to continue to be responsive to your user while the query executes in a separate thread. In your main thread, you can set a timer to let you know if the query has taken longer than your desired threshold, you can keep the user posted with the status of the query, and when the query finally completes, you can present the user with the results.
The ECMAScript version of the client object model and the Silverlight version (when it modifies the user interface) use asynchronous processing. The topic Data Retrieval Overview in the SharePoint SDK contains examples of how to use asynchronous processing using ECMAScript and Silverlight.
When building a traditional .NET application, such as a Windows Forms or WPF application, you may want to use asynchronous processing. The following example uses the BeginInvoke method to execute a query asynchronously. You will notice that the code passes a statement lambda expression to BeginInvoke, which makes it convenient to code this pattern, because the statement lambda expression can reference automatic variables in the method that contains it. You can see that the statement lambda expression has access to the newListCollection variable. (C# closures make the language 'just work' the way we expect it to.)
using System;
using System.Collections.Generic;
using Microsoft.SharePoint.Client;
class Program
{
static void Main(string[] args)
{
AsynchronousAccess asynchronousAccess = new AsynchronousAccess();
asynchronousAccess.Run();
Console.WriteLine("Before exiting Main");
Console.WriteLine();
Console.WriteLine("In a real application, the application can");
Console.WriteLine("continue to be responsive to the user.");
Console.WriteLine();
Console.ReadKey();
}
}
class AsynchronousAccess
{
delegate void AsynchronousDelegate();
public void Run()
{
Console.WriteLine("About to start a query that will take a long time.");
Console.WriteLine();
ClientContext clientContext =
new ClientContext("http://intranet.contoso.com");
ListCollection lists = clientContext.Web.Lists;
IEnumerable<List> newListCollection = clientContext.LoadQuery(
lists.Include(
list => list.Title));
AsynchronousDelegate executeQueryAsynchronously =
new AsynchronousDelegate(clientContext.ExecuteQuery);
executeQueryAsynchronously.BeginInvoke(
arg =>
{
Console.WriteLine("Long running query has completed.");
foreach (List list in newListCollection)
Console.WriteLine("Title: {0}", list.Title);
}, null);
}
}
[Download Code]
The example produces the following output:
About to start a query that will take a long time.
Before exiting Main
In a real application, the application can
continue to be responsive to the user.
Long running query has completed.
Title: Announcements
Title: Cache Profiles
Title: Calendar
Title: Client API Test List
Title: Content and Structure Reports
Title: Content type publishing error log
Title: Converted Forms
Title: Customized Reports
Title: Eric's ToDo List
Title: Eric's Wiki
Title: Form Templates
Title: Links
Other Resources
The SharePoint SDK contains some good resources and example code for the client object model that will be useful in a variety of scenarios:
SharePoint 2010 Developer Center
How to: Work with Web Sites
How to: Work with Users and Groups
How to: Break Role Assignment Inheritance
How to: Work with User Custom Actions
How to: Work with WebParts on a Page
Setting Up a Basic ASPX Page for ECMAScript
Using the Silverlight Object Model