Using the SharePoint Foundation 2010 Managed Client Object Model
Summary: Learn how to use the SharePoint Foundation 2010 managed client object model to write applications that are based on the .NET Framework and access SharePoint content from clients without installing code on the server that runs SharePoint Foundation 2010.
Applies to: Business Connectivity Services | Office 2010 | Open XML | SharePoint Designer 2010 | SharePoint Foundation 2010 | SharePoint Online | SharePoint Server 2010 | Visual Studio
Provided by: Eric White, Microsoft Corporation
Contents
Overview
Using the SharePoint Foundation 2010 Managed Client Object Model
How the Client-Side Object Model Works
Creating Windows Console Managed Client Object Model Applications
Overview of the SharePoint Foundation 2010 Managed Client Object Model
Using Object Identities
Trimming Result Sets
Creating and Populating a List
Using CAML to Query Lists
Filtering the Child Collection Returned by Load by Using LINQ
Using the LoadQuery Method
Increasing Performance by Nesting Include Statements in LoadQuery
Filtering the Child Collection Returned by LoadQuery by Using LINQ
Updating Client Objects
Deleting Client Objects
Discovering the Schema for Fields
Accessing Large Lists
Asynchronous Processing
Additional Resources
Overview
With the SharePoint Foundation 2010 managed client object model, you can design client applications that access SharePoint content without installing code on the server that runs Microsoft SharePoint Foundation 2010. For example, you can build new categories of applications that include writing applications that are based on the Microsoft .NET Framework, rich interactive Web Parts, Microsoft Silverlight applications, and ECMAScript (JavaScript, JScript) applications that run client-side in a SharePoint Web Part. For example:
A team leader creates a SharePoint site that has many lists that are required to manage her team’s mission. She wants to change these lists in an ad-hoc manner—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 a SharePoint deployment that brings list content 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 use the SharePoint Foundation 2010 managed client object model to achieve their goals. The SharePoint Foundation 2010 managed client object model lets you write client-side code to work with all the common objects in SharePoint sites. Through the object model, you can add and remove lists, add, update, and delete list items, change documents in document libraries, create sites, manage permissions of items, and add and remove Web Parts from a page.
Previously, there were few options. You could use Web services to interact with SharePoint lists and other features, but this was challenging. If the Web services did not provide the necessary capabilities, you 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 that sometimes that was not an option. The SharePoint Foundation 2010 managed client object model enables new kinds of applications and makes it easier to write client-side code that interacts with SharePoint content.
Using the SharePoint Foundation 2010 Managed Client Object Model
To use the SharePoint Foundation 2010 managed client object model (client object model), you write managed code based on the .NET Framework that uses an API that resembles the object model that is used on a server that is running SharePoint Foundation. The client object model has classes for accessing site collection information, site information, and list and list item information.
In the case of Web Parts, you use an ECMAScript (JavaScript, JScript) programming interface that resembles the .NET Framework API. For Silverlight, you use a subset of the API that is available through the .NET Framework on the client. Although much of the information that is presented in this article is relevant to the JavaScript and Silverlight APIs, this article focuses primarily about how to use the SharePoint Foundation 2010 managed client object model from a client application that is based on the .NET Framework.
The SharePoint Foundation 2010 managed client object model consists of two assemblies that contain five namespaces. If you look at the classes available in those namespaces, you see many classes. Do not worry; many of those classes are used internally by the object model. You are interested only in a subset of them, primarily classes that have direct counterparts to some familiar classes in the SharePoint Foundation server object model.
Table 1. Client-side classes and server-side equivalents
Client |
Server |
---|---|
Notice that the SharePoint Foundation 2010 managed 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 you must use the Site and Web classes to declare them.
The following example shows how I name variables.
ClientContext clientContext = new ClientContext(siteUrl);
Site siteCollection = clientContext.Site;
Web site = clientContext.Web;
How the Client-Side Object Model Works
An application that uses SharePoint content interacts with the API in several ways—call methods and get the return values, pass a Collaborative Application Markup Language (CAML) query and get the results, and set or get properties. After you use the API to perform a specific task the SharePoint Foundation 2010 managed client object model bundles up these uses of the API into XML and sends it to the server that runs SharePoint Foundation. The server receives this request, and makes appropriate calls into the object model on the server, collects the responses, forms them into JavaScript Object Notation (JSON), and sends that JSON back to the SharePoint Foundation 2010 managed client object model. The client object model parses the JSON and presents the results to the application as .NET Framework objects (or JavaScript objects for JavaScript). The following diagram shows these interactions.
Figure 1. The SharePoint Foundation 2010 managed client object model
It is important to be aware that you control when the SharePoint Foundation 2010 managed client object model starts to send the XML to the server and receives the JSON back from the server.
The bundling of multiple method calls into a single call to the server is dictated by the realities of network speed, network latency, and desired performance characteristics. If the SharePoint Foundation 2010 managed client object model interacted with the server at every method call, the performance of the system, and the increased network traffic would make the system unworkable.
As I mentioned, you explicitly control when the SharePoint Foundation 2010 managed client object model bundles method calls and sends a request to the server. As part of this process, before you start the interaction with the server, you must explicitly specify what content that you want to retrieve from the server. This is the biggest difference between the SharePoint Foundation 2010 managed client object model and the SharePoint Foundation 2010 object model. But after you understand the model, it is not challenging. The easiest way to start understanding the difference is to see a simple application.
Creating Windows Console Managed Client Object Model Applications
Note
I use Windows console applications for the sample code, but you can use the same approach with other application types.
To build the application, you must add references to two assemblies, Microsoft.SharePoint.Client.dll and Microsoft.SharePoint.Client.Runtime.dll. Installing SharePoint Foundation installs these assemblies on the server. The two assemblies are located in the following directory:
%ProgramFiles%\Common Files\Microsoft Shared\web server extensions\14\ISAPI
Important
For SharePoint Foundation Beta and Microsoft SharePoint Server 2010 Beta, you must copy the two assemblies and put them in a convenient location on your development client computer. You must browse to those assemblies to add references to them when you are setting up projects that use the SharePoint Foundation 2010 managed 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 then click Windows.
To the right of the Recent Template pane, click Console Application.
By default, Visual Studio creates a project that targets .NET Framework 4, but you must target .NET Framework 3.5. From the list at the upper part of the File Open dialog box, select .NET Framework 3.5.
In the Name box, type the name that you want to use for your project, such as FirstClientApiApplication.
In the Location box, type the location where you want to place the project.
Figure 2. Creating a solution in the New Project dialog box
Click OK to create the solution.
To add references to the Microsoft.SharePoint.Client assembly and Microsoft.SharePoint.Client.Runtime assembly
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 you add the references, you must copy those assemblies from the server that runs SharePoint Foundation to the 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 put the Microsoft.SharePoint.Client.dll and Microsoft.SharePoint.Client.Runtime.dll. Select both DLLs, and then click OK as shown in Figure 3.
Figure 3. Adding references to the assemblies
To 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); } }
Replace the URL in the ClientContext(String) constructor with the URL to the SharePoint site. Build and run the solution. The example prints the title of the site.
Just as with the SharePoint Foundation server 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 the ExecuteQuery() method causes the SharePoint Foundation 2010 managed client object model to send the request to the server. There is no network traffic until the application calls the ExecuteQuery() method.
An important point to make about this example is that the call to the Load() method does not actually load anything. Instead, it informs the client object model that when the application calls the 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 SharePoint Foundation 2010 managed 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 class, ListItem class, and Web class), CAML queries that you want to run, and objects such as ListItem objects that you want to insert, update or delete.
Then you call the ExecuteQuery() method. No network traffic occurs until you call the ExecuteQuery() method. Until that point, your application is only registering its requests.
As you can see from this example, at its simplest, you first set up a query, and then you execute the queries. This causes the client object model to send traffic to the server and receive a response from it. This next section reviews the model in detail and shows why it is designed the way it is, and finally, how you can build applications by using the model.
Overview of the SharePoint Foundation 2010 Managed Client Object Model
There are specific aspects of the client object model that you must examine:
Approaches that the client object model takes to minimize network traffic
Query construction
Techniques to improve server performance
How to create, update, and delete client objects
How to work with very large lists
Before we delve into any of these subjects, let's examine the issue of object identity.
Using Object Identities
The key idea behind object identity is that client objects refer to the corresponding in the SharePoint Foundation server object model both before and after they call the ExecuteQuery() method. They continue to refer to that same object through multiple calls to the ExecuteQuery() method.
This means that when setting up the query, the client object model returns objects that you can use to further set up the query before you call the ExecuteQuery() method. This lets you to write more complex queries before you start causing traffic to and from the server. You can do more interesting things in a single query, and eliminate network traffic.
The following example gets the Announcements list object, and then it retrieves all items of that list by 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, oListItem["Title"]);
}
}
Notice the sequence in this example:
First the code gets a List object by using the GetByTitle() method. Remember, this List object has no data in it; it does not 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 is not populated with data.
It finally calls the Load() method on both the list object and listItems object, 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 by using the GetByTitle() method, and that the client object model should execute the CAML query on that same list object after the list object is retrieved from the SharePoint database. Any class that derives from the ClientObject class has these semantics.
And, as mentioned, you can continue to use client objects to set up additional queries after you call the ExecuteQuery() method. In the following example, the code loads the list object and calls the ExecuteQuery() method. It then uses that list client object to call the List.GetItems method, and then calls the ExecuteQuery() method again. The list object retained its identity through the call to ExecuteQuery() method.
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}",
oListItem.Id, listItem["Title"]);
}
}
Some properties and methods return objects or value types that do not derive from the ClientObject class. You 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 the FieldUrlValue class and the FieldLookupValue class derive from the ClientValueObject class, and you cannot use properties that return those types until after the call to the ExecuteQuery() method. Some properties return .NET Framework types such as string or integer, and you also cannot use properties or methods that return those until after the call to the ExecuteQuery() method. Because you cannot use the values of any properties until those values are populated in the ExecuteQuery() call, you cannot, for example, find an item in a list, and use the value of one of that item’s fields to select items in an additional query. If you try to use a property before it is populated by the ExecuteQuery() method, the client object model throws a PropertyOrFieldNotInitializedException exception.
Warning
Client object identity is valid only for a single ClientContext object. If you initialize another ClientContext object to the same SharePoint site, you cannot use client objects from one client context with the other one.
A number of later examples in this article use object identity behavior.
Note
This example does not do any error handling. If the Announcements list does not exist, the client object model throws 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 Foundation is often deployed in organizations with many thousands of users. When building an application that accesses SharePoint Foundation over the network, it makes sense to build it so that it uses the least amount of network traffic. There are several ways that the client object model helps you 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 must load only the Title property and Description property. This reduces the size of the JSON response from the server back to the client.
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);
}
}
By default, if you do not include these lambda expressions in the call to the Load() method, it loads a much larger number of properties (but not them all). The first two examples called the Load() method without specifying which properties to load, so that the JSON packet that the server returned was somewhat larger than it needed to be. Although in these small examples, it does not make much difference, when it loads thousands of list items, carefully specifying the required properties reduces network traffic.
Using lambda expressions, you can specify a list of properties to the Load() method. Reducing network traffic is not the only benefit you derive from the client object model’s use of lambda expressions. Later on this article describes how to filter result sets using lambda expressions.
Next, I’ll show an example that creates a list and then add some content to it. This will provide sample content to work with for the rest of this article.
Creating and Populating a List
The following example creates a list, and adds some fields and several 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 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();
}
}
In many cases, where you can create a client object, the application can call an Add method that takes as an argument an object that specifies creation information. This example shows how to use ListCreationInformation class to create a List object, and how to use the ListItemCreationInformation class for creating a ListItem object. You often set properties of the creation information class after instantiating it. You can see that the code sets the Title property and TemplateType property of the ListItemCreationInformation object. Note that to create a list, you call the Add() method, but to create a ListItem object, you call the AddItem() method. The Add() method creates a list in the collection, and the AddItem() method creates a single list item.
Creating fields in a list also does not use an Add method, because, when you create fields, you are not really creating an instance of the Field class. You are creating an instance of a class that derives from the Field class. There are many options available for those derived classes, and using an Add method would significantly complicate the design of a FieldCreationInformation class. For this reason, the client object model does not include such a class. Instead, the simplest way to create a field is to specify a bit of XML that defines the field, and pass that XML to the AddFieldAsXml() method. There is a Add() method that you 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.
Tip
The Discovering the Schema for Fields section of this article shows you an easy way to discover the XML that you must specify for fields that you want to create.
Note that, of course, no objects are actually added to the SharePoint database until the application calls the ExecuteQuery() method.
There is one more item of interest in this example. Notice that after you call the AddItem() method, the example sets three indexed properties. The code is setting the values of the fields that were previously added to the list. After setting these properties, the application must call the Update() method, informing the client object model that those objects were modified. The client object model does not work correctly if you do not do so. You see the Update() method used in later examples, when I show how to modify existing client objects.
Now that you have some data, let us discover some interesting ways to query and change it.
Using CAML to Query Lists
The following example shows how to use CAML to query the list that you created in the last example. 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();
}
}
}
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 a difference between the lambda expressions that you specify in this example and the lambda expressions in the example that is presented in the Trimming Result Sets section. You must use the Include() extension method to specify the properties that you want to load for each item in the collection that we load. The items parameter of the lambda expression is of type ListItemCollection, which of course does not contain an indexed property that allows us to specify which properties to load for items in the collection. Instead, you call the Include() extension method, which enables 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. Therefore, you can specify the properties that you want to load for each item in the collection.
Again, it is not completely 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 add 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 item in a collection of client objects, 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"]));
Filtering the Child Collection Returned by Load by Using LINQ
Because the Include() extension method returns IQueryable<T>, you can chain from the Include() method into the IQueryable<T>.Where extension method. This provides a succinct way to filter the result set. You should only use this capability when querying client object collections other than collections of ListItem objects, because while you could use this technique to filter collections of ListItem objects, using CAML results in better performance. This is important enough to say again:
Warning
Never use the IQueryable<T>.Where extension method when querying ListItem objects. The reason is that 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 a very large list using LINQ instead of CAML, the client object model attempts to retrieve all items in the list before filtering with LINQ and either issues queries that require too much system resources, or the query fails. The reason is not clear unless you know how the client object model works internally. You must use CAML when querying list items.
The following example queries the client object model for all lists that are not hidden. Notice that you must include a using directive for the System.Linq namespace.
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);
}
}
On my server, 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. It also allows for a more flexible programming style.
The LoadQuery() method 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 a new collection. This means that you can query the same object collection multiple times and keep 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 larger than a certain threshold, and access both result sets at the same time. It also enables you to let these collections go out of scope, and thereby become eligible for garbage collection. Collections that you load using the Load() method are eligible for garbage collection only when the client context variable itself goes out of scope. Other than these differences, the LoadQuery() method provides the same functionality as the Load() method.
The following example uses the LoadQuery() method 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"));
}
}
Notice that the LoadQuery() method returns a new list collection that you can iterate through. The new list collection has a type of IEnumerable<List> instead of ListCollection.
There is one aspect of the semantics of the LoadQuery() method that you must pay attention to. In the previous example, the original lists variable does not have its property values populated after the ExecuteQuery() method returns. If you want that list to be populated, you must explicitly call the Load() method, specifying which properties that you want loaded.
Increasing Performance by Nesting Include Statements in LoadQuery
When calling the LoadQuery() method, you can specify multiple levels of properties to load. This allows the client object model to optimize its access to the server that runs SharePoint Foundation by reducing the number of times the client object model must call to the server that runs SharePoint Foundation to retrieve the data that you want. The following query retrieves all lists from the site, and all fields from each list. It then prints them to the console and indicates 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);
}
}
}
This approach allows the server part 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 by Using LINQ
The LoadQuery() method takes an object of type IQueryable<T> as its parameter, and this enables you 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);
}
}
Updating Client Objects
Updating client objects by using the client object model is fairly simple. You retrieve the objects, alter properties, call the Update method for each object that you 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 percent (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();
}
}
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. You cannot iterate through the collection, deleting objects. As soon as you 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, you must 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 you 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();
}
}
The following code example shows the incorrect approach.
clientContext.Load(
listItems,
items => items.Include(
item => item["Title"]));
clientContext.ExecuteQuery();
// The ToList() method call is removed in the following line.
foreach (ListItem listItem in listItems)
listItem.DeleteObject();
clientContext.ExecuteQuery();
Finally, 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();
}
}
Discovering the Schema for Fields
As promised, this section shows an easy way to discover the XML schema that you use to create the fields that you want to create in a list. First, on the SharePoint site, create a list that contains columns that are configured as you want them. Then you can then use the following example to output the XML that creates 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("===============");
}
}
}
}
When you run this after you create the list by 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 are not required 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 how much 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 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();
}
}
}
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 must attach to SharePoint sites that may not be available, or if you must regularly invoke queries that may take a long time, you should consider using asynchronous processing. This enables your application 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 is taking longer than your desired threshold, you can keep the user posted with the status of the query, and when the query finally finishes, you can display the results.
The JavaScript 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 2010 SDK contains examples of how to use asynchronous processing using JavaScript and Silverlight.
When building a traditional application that is based on the .NET Framework, 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. Notice that the code passes a statement lambda expression to the BeginInvoke method, which makes it convenient to create this pattern, because the statement lambda expression can refer to automatic variables in the method that contains it. You can see that the statement lambda expression has access to the clientContext variable and the newListCollection variable. Microsoft Visual C# closures make the language function as you expect.
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 =>
{
clientContext.ExecuteQuery();
Console.WriteLine("Long running query completed.");
foreach (List list in newListCollection)
Console.WriteLine("Title: {0}", list.Title);
}, null);
}
}
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 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
Additional Resources
For more information, see the following resources: