SharePoint 2010 Client Object Model
In Part 1 of this article I took an in-depth look at the SharePoint Client Object
Model; how it works and how to form queries and work with objects. Now I'll take
a practical look at using the Client OM with examples using .NET
Managed code, using WPF, JavaScript used in a Application page, and finally with
Silverlight in a SharePoint webpart.
Setup
To facilitate the examples the first thing was to create a SharePoint
project that creates a simple Contact list and populates it with a few Contacts
to begin with. This project is included in the downloads for this article. For
the Silverlight example I created a Silverlight project and added the output to
a Document Library on the site.
The demo apps may not be the best designed, but they are functional and
effectively demonstrate using the Client Object Model in a somewhat pratical
manner.
Using Client OM with .NET Managed Code
This application first gets a ListItemCollection containing all the
Contacts in the demo list.
public static ListItemCollection GetList()
{
using(ClientContext ctx = new ClientContext(SITE))
{
Web web = ctx.Web;
List list = web.Lists.GetByTitle(LIST_NAME);
CamlQuery query = new CamlQuery();
query.ViewXml = "<View>" +
"<Query>" +
"<OrderBy>" +
"<FieldRef Name='Title'/>" +
"<FieldRef Name='FirstName'/>" +
"</OrderBy>" +
"</Query>" +
"<ViewFields>" +
"<FieldRef Name='ID'/>" +
"<FieldRef Name='Title'/>" +
"<FieldRef Name='FirstName'/>" +
"<FieldRef Name='WorkAddress'/>" +
"<FieldRef Name='WorkCity'/>" +
"<FieldRef Name='WorkState'/>" +
"<FieldRef Name='WorkZip'/>" +
"</ViewFields>" +
"</View>";
ListItemCollection listItems = list.GetItems(query);
ctx.Load(listItems);
ctx.ExecuteQuery();
return listItems;
}
}
Since this article is more about usage of the Client Object Model I won't delve into the XAML too much. Just to show, however, the ListBox is bound to the ListItemCollection returned from the above code
and the TextBlocks in the DataTemplate are bound to the indexer property of the ListItem for
the specified FieldValue
<ListBox x:Name="Contacts" ItemsSource="{Binding}" Width="225" Height="300" SelectionChanged="Contacts_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=[FirstName]}" Margin="0,0,5,0"/>
<TextBlock Text="{Binding Path=[Title]}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Part 1 of this article covers the details for retrieving ListItems, ClientContext, CamlQuery and other objects and methods being used so I won't go into them here.
Updating ListItems
Clicking on the Edit button in the application causes the edit fields to be
displayed, once again I'm not focusing on the WPF, you can explore the download
files for more details on that aspect. After clicking the Save button it's simply
a matter of finding the ListItem to be updated, update the FieldValues, then commit the changes.
public static void Update(int id, string firstName, string lastName, string address,
string city, string state, string postalCode)
{
using(ClientContext ctx = new ClientContext(SITE))
{
Web web = ctx.Web;
List list = web.Lists.GetByTitle(LIST_NAME);
ListItem item = list.GetItemById(id);
item["Title"] = lastName;
item["FirstName"] = firstName;
item["WorkAddress"] = address;
item["WorkCity"] = city;
item["WorkState"] = state;
item["WorkZip"] = postalCode;
item.Update();
ctx.ExecuteQuery();
}
}
One thing to note here the lack of any calls to Load or LoadQuery as in the examples
in Part 1. Since nothing is being returned in this case there is no need to use these methods. Think of it as the equivalent of the ADO.NET ExecuteNonQuery method.
Another thing to note is although it may seem tempting to cache the ListItem and update it directly,
without the GetItemById call, this won't work. The item must be
retrieved within the context of the ClientContext object, similar to how the Entity Framework functions for those familiar with it. It is not recommended to cache the ClientContext since it does hold resources
and may cause degradation of your application. Best to follow the database
connection model; open late, use and close.
Adding ListItems
Adding a new ListItem to the List is almost as easy as updating. One of the main differences though is the ListItemCreationInformation object.
public static void AddContact(string firstName, string lastName, string address,
string city, string state, string postalCode)
{
using(ClientContext ctx = new ClientContext(SITE))
{
Web web = ctx.Web;
List list = web.Lists.GetByTitle(LIST_NAME);
ListItemCreationInformation createInfo = null;
ListItem item = list.AddItem(createInfo);
item["Title"] = lastName;
item["FirstName"] = firstName;
item["WorkAddress"] = address;
item["WorkCity"] = city;
item["WorkState"] = state;
item["WorkZip"] = postalCode;
item.Update();
ctx.ExecuteQuery();
}
}
Just as with the methods demonstrated in Part 1 when the ExecuteQuery method is invoked the Client OM API will create an XML string and POST it to the client.svc Web Service.
As you can see below this is very similar to the "get" methods but you'll notice
the Method element specifying which method is to be called and the Parameter elements passing the type and value.
POST http:X-RequestDigest: 0xE8312251E4B[truncated for space]0000
Content-Type: text/xml
X-RequestForceAuthentication: true
Host: mySite
Content-Length: 2461
Expect: 100-continue
<Request AddExpandoFieldTypeSuffix="true" SchemaVersion="14.0.0.0" LibraryVersion="14.0.4762.1000"
ApplicationName=".NET Library" xmlns="http://schemas.microsoft.com/sharepoint/clientquery/2009">
<Actions>
<ObjectPath Id="25" ObjectPathId="24" />
<ObjectPath Id="27" ObjectPathId="26" />
<ObjectPath Id="29" ObjectPathId="28" />
<ObjectPath Id="31" ObjectPathId="30" />
<ObjectIdentityQuery Id="32" ObjectPathId="30" />
<ObjectPath Id="34" ObjectPathId="33" />
<Method Name="SetFieldValue" Id="35" ObjectPathId="33">
<Parameters>
<Parameter Type="String">Title</Parameter>
<Parameter Type="String">Betic</Parameter>
</Parameters>
</Method>
<Method Name="SetFieldValue" Id="36" ObjectPathId="33">
<Parameters>
<Parameter Type="String">FirstName</Parameter>
<Parameter Type="String">Alpha</Parameter>
</Parameters>
</Method>
<Method Name="SetFieldValue" Id="37" ObjectPathId="33">
<Parameters>
<Parameter Type="String">WorkAddress</Parameter>
<Parameter Type="String">Main street </Parameter>
</Parameters>
</Method>
<Method Name="SetFieldValue" Id="38" ObjectPathId="33">
<Parameters>
<Parameter Type="String">WorkCity</Parameter>
<Parameter Type="String">Anytown</Parameter>
</Parameters>
</Method>
<Method Name="SetFieldValue" Id="39" ObjectPathId="33">
<Parameters>
<Parameter Type="String">WorkState</Parameter>
<Parameter Type="String">PA</Parameter>
</Parameters>
</Method>
<Method Name="SetFieldValue" Id="40" ObjectPathId="33">
<Parameters>
<Parameter Type="String">WorkZip</Parameter>
<Parameter Type="String">12345</Parameter>
</Parameters>
</Method>
<Method Name="Update" Id="41" ObjectPathId="33" />
<Query Id="42" ObjectPathId="33">
<Query SelectAllProperties="false">
<Properties>
<Property Name="Title" ScalarProperty="true" />
<Property Name="FirstName" ScalarProperty="true" />
<Property Name="WorkAddress" ScalarProperty="true" />
<Property Name="WorkCity" ScalarProperty="true" />
<Property Name="WorkState" ScalarProperty="true" />
<Property Name="WorkZip" ScalarProperty="true" />
</Properties>
</Query>
</Query>
</Actions>
<ObjectPaths>
<StaticProperty Id="24" TypeId="{3747adcd-a3c3-41b9-bfab-4a64dd2f1e0a}" Name="Current" />
<Property Id="26" ParentId="24" Name="Web" />
<Property Id="28" ParentId="26" Name="Lists" />
<Method Id="30" ParentId="28" Name="GetByTitle">
<Parameters>
<Parameter Type="String">SPUG Demo Contacts</Parameter>
</Parameters>
</Method>
<Method Id="33" ParentId="30" Name="GetItemById">
<Parameters>
<Parameter Type="Int32">4</Parameter>
</Parameters>
</Method>
</ObjectPaths>
</Request>
Although nothing is returned, such as with Load or LoadQuery, there is a response from the Client.svc Web Service call. As you see below the response simply contains the object that was updated, however not the _ObjectVersion_ property which incremented with the version of the object returned.
HTTP/1.1 200 OK
Cache-Control: private
Content-Type: application/json
Server: Microsoft-IIS/7.5
SPRequestGuid: 472b1d44-cc1e-4295-a7b0-c38cf3b68a62
Set-Cookie: WSS_KeepSessionAuthenticated={4e4cffb3-203e-4857-8f5a-90a4b18789a4}; path=/
X-SharePointHealthScore: 0
X-Content-Type-Options: nosniff
X-AspNet-Version: 2.0.50727
X-Powered-By: ASP.NET
MicrosoftSharePointTeamServices: 14.0.0.6029
Content-Length: 672
[
{
"SchemaVersion":"14.0.0.0","LibraryVersion":"14.0.6106.5001","ErrorInfo":null
},25,{
"IsNull":false
},27,{
"IsNull":false
},29,{
"IsNull":false
},31,{
"IsNull":false
},32,{
"_ObjectIdentity_":"740c6a0b-85e2-48a0-a494-e0f1759d4aa7:web:6bb[truncated for space]3964:list:9bf3[truncated for space]d045"
},34,{
"IsNull":false
},42,{
"_ObjectType_":"SP.ListItem",
"_ObjectIdentity_":"740c6a0b-85e2-48a0-a494-e0f1759d4aa7:web:6bb[truncated for space]964:list:9bf[truncated for space]d045:item:4,1",
"_ObjectVersion_":"2",
"Title":"Betic",
"FirstName":"Alpha",
"WorkAddress":"Main street ",
"WorkCity":"Anytown",
"WorkState":"PA",
"WorkZip":"12345"
}
]
Deleting ListItems
Deleting a ListItem is the simplest of the operations. After obtaining the ListItem, call it's DeleteObject method and commit the changes with an ExecuteQuery.
public static void Delete(int id)
{
using(ClientContext ctx = new ClientContext(SITE))
{
Web web = ctx.Web;
List list = web.Lists.GetByTitle(LIST_NAME);
ListItem item = list.GetItemById(id);
item.DeleteObject();
ctx.ExecuteQuery();
}
}
POST http:X-RequestDigest: 0x2BD0E4[truncated for space]0000
Content-Type: text/xml
X-RequestForceAuthentication: true
Host: mySite
Content-Length: 994
Expect: 100-continue
<Request AddExpandoFieldTypeSuffix="true" SchemaVersion="14.0.0.0" LibraryVersion="14.0.4762.1000"
ApplicationName=".NET Library" xmlns="http://schemas.microsoft.com/sharepoint/clientquery/2009">
<Actions>
<ObjectPath Id="54" ObjectPathId="53" />
<ObjectPath Id="56" ObjectPathId="55" />
<ObjectPath Id="58" ObjectPathId="57" />
<ObjectPath Id="60" ObjectPathId="59" />
<ObjectIdentityQuery Id="61" ObjectPathId="59" />
<ObjectPath Id="63" ObjectPathId="62" />
<Method Name="DeleteObject" Id="64" ObjectPathId="62" />
</Actions>
<ObjectPaths>
<StaticProperty Id="53" TypeId="{3747adcd-a3c3-41b9-bfab-4a64dd2f1e0a}" Name="Current" />
<Property Id="55" ParentId="53" Name="Web" />
<Property Id="57" ParentId="55" Name="Lists" />
<Method Id="59" ParentId="57" Name="GetByTitle">
<Parameters>
<Parameter Type="String">SPUG Demo Contacts</Parameter>
</Parameters>
</Method>
<Method Id="62" ParentId="59" Name="GetItemById">
<Parameters>
<Parameter Type="Int32">6</Parameter>
</Parameters>
</Method>
</ObjectPaths>
</Request>
Once again, although nothing is returned a response is sent from the Web Service.
This time though there is no object since it has been deleted.
HTTP/1.1 200 OK
Cache-Control: private
Content-Type: application/json
Server: Microsoft-IIS/7.5
SPRequestGuid: 28594025-6d96-4798-ba72-06a6b81f0c3e
Set-Cookie: WSS_KeepSessionAuthenticated={4e4cffb3-203e-4857-8f5a-90a4b18789a4}; path=/
X-SharePointHealthScore: 0
X-Content-Type-Options: nosniff
X-AspNet-Version: 2.0.50727
X-Powered-By: ASP.NET
MicrosoftSharePointTeamServices: 14.0.0.6029
Content-Length: 343
[
{
"SchemaVersion":"14.0.0.0","LibraryVersion":"14.0.6106.5001","ErrorInfo":null
},54,{
"IsNull":false
},56,{
"IsNull":false
},58,{
"IsNull":false
},60,{
"IsNull":false
},61,{
"_ObjectIdentity_":"740c6a0b-85e2-48a0-a494-e0f1759d4aa7:web:6bb[truncated for space]964:list:9bf[truncated for space]d045"
},63,{
"IsNull":false
}
]
Client OM with JavaScript
Working with the Client OM in JavaScript is quite similar to Managed Code. The objects are essentially the same, except the method names conform to JavaScript coding standards.
One major difference, however, is the Client OM implementation in JavaScript
uses asynchronous methods where the Managed Code implementation uses synchronous
methods. In Managed Code though this doesn't prevent you from using Asynchronous
methods when implementing your code as the Silverlight example will show.
function loadContactsList()
{
var ctx = SP.ClientContext.get_current();
var web = ctx.get_web();
var list = web.get_lists().getByTitle(LIST_NAME);
var camlQuery = new SP.CamlQuery();
var queryXml = "<View>" +
"<Query>" +
"<OrderBy>" +
"<FieldRef Name='Title'/>" +
"<FieldRef Name='FirstName'/>" +
"</OrderBy>" +
"</Query>" +
"</View>";
camlQuery.set_viewXml(queryXml);
this.listItems = list.getItems(camlQuery);
ctx.load(this.listItems, 'Include(ID, Title, FirstName)');
ctx.executeQueryAsync(
Function.createDelegate(this, onLoadContactsSuccess),
Function.createDelegate(this, onFail));
}
As you can see in the above code querying for the ListItems is the same as in the WPF example; get a ClientContext, get the objects necessary, form a CamlQuery and call executeQueryAsync. As you'll notice, getting the ClientContext with JavaScript is slightly different.
Just as with the Managed Code implementation you can construct a ClientContext object by passing a URL. However, in JavaScript this is
a relative URL, rather than a full URL, since the code is executing with a SharePoint site already.
var ctx = SP.ClientContext("serverRelativeUrl");
The get_current method is particular to the JavaScript Client OM implementation and, as you can see below, constructs the ClientContext object from the get_webServerRelativeUrl
method which has been filled in from other SharePoint JavaScript code.
SP.ClientContext.get_current = function() {ULS5Vl:;
if (!SP.ClientContext.$1S_1) {
SP.ClientContext.$1S_1 = new SP.ClientContext(SP.PageContextInfo.get_webServerRelativeUrl());
}
return SP.ClientContext.$1S_1;
}
After executeQueryAsync has been called successfully the delegate specified in the first parameter, onLoadContactsSuccess in this case, will be called.
function onLoadContactsSuccess(sender, args)
{
$("#contactList").empty();
if(this.listItems.get_count() == 0)
{
$("#contactList").append("<span>No contacts found</span>");
}
else
{
var listEnumerator = this.listItems.getEnumerator();
while (listEnumerator.moveNext())
{
var item = listEnumerator.get_current();
var title = item.get_fieldValues().Title;
var firstName = item.get_fieldValues().FirstName;
var id = item.get_fieldValues().ID;
var contact = "<span önclick='displayContactDetails(" + id + ")'>" + firstName + " " + title + "</span>";
$("#contactList").append(contact + "<br/>");
}
}
}
Getting an individual ListItem is again almost the same as previous examples. The load method, however, is a little different. Since JavaScript doesn't support Lambda expression
you can pass an array of strings that specify the columns to be returned in the
object.
function displayContactDetails(id)
{
selectedItemId = id;
var ctx = SP.ClientContext.get_current();
var web = ctx.get_web();
var list = web.get_lists().getByTitle(LIST_NAME);
listItem = list.getItemById(id);
ctx.load(this.listItem, "Title", "FirstName", "WorkAddress", "WorkCity", "WorkState", "WorkZip");
ctx.executeQueryAsync(
Function.createDelegate(this, onContactDetailsSuccess),
Function.createDelegate(this, onFail));
}
Updating ListItem
No real surprises here. The difference with JavaScript is using the set_item method rather than using the indexer in Managed Code.
function saveContact()
{
$(".field").each(function ()
{
var id = $(this).attr("ID");
var value = $(this).next("input").val();
listItem.set_item(id,value);
});
listItem.update();
var ctx = SP.ClientContext.get_current();
ctx.executeQueryAsync(
Function.createDelegate(this, onSaveContactSuccess),
Function.createDelegate(this, onFail));
}
Adding ListItems
By now you should be getting the hang of this.
function onAdd()
{
var ctx = SP.ClientContext.get_current();
var web = ctx.get_web();
var list = web.get_lists().getByTitle(LIST_NAME);
var createInfo = null;
var item = list.addItem(createInfo);
$(".field").each(function ()
{
var id = $(this).attr("ID");
var value = $(this).val();
item.set_item(id, value);
});
item.update();
ctx.executeQueryAsync(
Function.createDelegate(this, onAddContactSuccess),
Function.createDelegate(this, onFail));
}
Deleting ListItem
Just to complete the example
function onDelete()
{
var ctx = SP.ClientContext.get_current();
var web = ctx.get_web();
var list = web.get_lists().getByTitle(LIST_NAME);
var listItem = list.getItemById(selectedItemId);
listItem.deleteObject();
ctx.executeQueryAsync(
Function.createDelegate(this, onDeleteSuccess),
Function.createDelegate(this, onFail));
}
Client OM with Silverlight
By now you are probably bored working with ListItems using the Client OM. Since there isn't much difference with the previous methods I'll
use Silverlight to focus on other things that can be done with the Client OM; in Managed code, JavaScript or Silverlight.
Specifically, with Silverlight I'll demonstrate working with WebParts on a page.
Upon loading, this Silverlight WebPart will read the WebParts on the designated page, default.aspx in this example, and
display the titles in the ListBox.
private void LoadWebParts()
{
SelectedItem = null;
wpList.Items.Clear();
using(ClientContext ctx = new ClientContext(SITE))
{
try
{
File file = ctx.Web.GetFileByServerRelativeUrl(PAGE);
LimitedWebPartManager wpm = file.GetLimitedWebPartManager(PersonalizationScope.Shared);
WPDefinitions = ctx.LoadQuery(wpm.WebParts.Include(w => w.Id, w => w.WebPart));
ctx.ExecuteQueryAsync(OnLoadSucceeded, OnFail);
}
catch(System.Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
As you can see, like all of the other examples, you first get a CLientContext to work with, using a server relative URL since the page is operating within a SharePoint site already.
Next is to get the File to work with by using the well named method GetFileByServerRelativeUrl. After this is getting the LimitedWebPartManager to obtain a collection of WebPartDefinitions from via the LoadQuery method.
As with other examples I'm using a Lambda expression to limit the results to
only what is necessary, the ID and WebPart properties in this case. At this point using the Client OM with Silverlight will diverge from the other environments.
Since this code is being run from the main thread of the application you'll need
to use the asynchronous method, ExecuteQueryAsync to make the query to prevent blocking the UI thread. This method takes delegates to ClientRequestSucceededEventHandler and ClientRequestFailedEventHandler
that will be called when the method succeeds or fails.
private void OnLoadSucceeded(object sender, ClientRequestSucceededEventArgs e)
{
Action updateList = () =>
{
if(WPDefinitions.Count() != 0)
{
foreach(WebPartDefinition def in WPDefinitions)
{
wpList.Items.Add(new ListItemHelper() { ID = def.Id, Title = def.WebPart.Title });
}
}
};
this.Dispatcher.BeginInvoke(updateList);
}
In this method I create an anonymous delegate to pass to the BeginInvoke method that will iterate through the WebPartDefinition collection populated by the LoadQuery call.
Adding WebPart
Using the Client OM to add WebParts to a page is relatively straight forward.
private void OnUpdate(object sender, System.Windows.RoutedEventArgs e)
{
SelectedItem.Title = wpTitle.Text;
SelectedItem.Zone = ((ComboBoxItem)wpZone.SelectedItem).Content.ToString();
ThreadPool.QueueUserWorkItem(new WaitCallback(CSOMHelper.UpdateTitle), SelectedItem);
wpList.Items[SelectedItem.ItemIndex] = wpTitle.Text;
wpTitle.Text = "";
}
In this example I'll use a slightly different method to call the Client OM
methods. Once again since this code is being executed in the main UI thread you
can't use any blocking methods. In this case I use a worker thread to do the
processing since there isn't anything that needs to be updated on the UI from
the Client OM code.
public static void UpdateTitle(Object state)
{
ListItemHelper item = (ListItemHelper)state;
using(ClientContext ctx = new ClientContext(SITE))
{
File file = ctx.Web.GetFileByServerRelativeUrl(PAGE);
LimitedWebPartManager wpm = file.GetLimitedWebPartManager(PersonalizationScope.Shared);
IEnumerable<WebPartDefinition> definitions = ctx.LoadQuery(wpm.WebParts.Include(w => w.Id, w => w.WebPart));
ctx.ExecuteQuery();
WebPartDefinition def = definitions.FirstOrDefault(d => d.Id == item.ID);
if(def != null)
{
def.WebPart.Title = item.Title;
def.MoveWebPartTo(item.Zone.ToLower(), 0);
def.SaveWebPartChanges();
ctx.ExecuteQuery();
}
}
}
This method is just like the load method, get a ClientContext and collection of WebPartDefinitions. After finding the WebPart being updated you set the Title property
and call the handy and well named method MoveWebPartTo to move the WebPart to a new WebPartZone. Again, the well named method SaveWebPartChanges saves the updates that have been made and the changes are committed by the call to ExecuteQuery. Since
this is being executed within the worker thread you can use the synchronous
method. This shows that both synchronous and asynchronous methods are available
in the Silverlight implementation of the Client OM, unlike Managed Code where
only synchronous methods are available and JavaScript where only asynchronous
methods are available.
Deleting WebParts
Deleting WebParts is just as easy. Follow the same steps as above and call the DeleteWebPart method.
WebPartDefinition def = definitions.FirstOrDefault(d => d.Id == item.ID);
if(def != null)
{
def.DeleteWebPart();
def.SaveWebPartChanges();
ctx.ExecuteQuery();
}
UserAction Menus
For the final example I'll demonstrate working with the UserActions menu to add items to the EditControlBlock menu of a list and the Site Actions menu.
public static void AddUserActions()
{
using(ClientContext ctx = new ClientContext(URL))
{
Web web = ctx.Web;
List list = web.Lists.GetByTitle(LIST_NAME);
UserCustomActionCollection actions = list.UserCustomActions;
UserCustomAction action = actions.Add();
action.Location = "EditControlBlock";
action.Sequence = 100;
action.Title = "SPUG Custom Action";
action.Url = URL + @"/default.aspx";
action.Update();
ctx.Load(list, l => l.UserCustomActions);
ctx.ExecuteQuery();
}
}
Adding a menu item to the Site Actions menu is same as the above example, only the Location and Group properties need to be changed.
UserCustomAction action = actions.Add();
action.Location = "Microsoft.SharePoint.StandardMenu";
action.Group = "SiteActions";
action.Sequence = 101;
action.Title = "SPUG Custom Action";
action.Url = URL + @"/default.aspx";
action.Update();
Removing the menu item just a matter of iterating through the UserCustomActions, finding the correct one and calling the DeleteObject.
This code will remove menu items from both the EditControlBlock and Site Actions
menus since it is only comparing the title. Obviously, you'll want to check the Location property to be more specific.
public static void RemoveUserActions()
{
using(ClientContext ctx = new ClientContext(URL))
{
Web web = ctx.Web;
List list = web.Lists.GetByTitle(LIST_NAME);
UserCustomActionCollection actions = list.UserCustomActions;
ctx.Load(list, l => l.UserCustomActions.Include(a => a.Title));
ctx.ExecuteQuery();
for(int x = 0; x < actions.Count; x++)
{
if(actions[x].Title == "SPUG Custom Action")
{
actions[x].DeleteObject();
ctx.ExecuteQuery();
break;
}
}
}
}
Creating a List
So far all of the examples have used previously created lists. Now I'll show how to create one and add Fields to it.
Before creating a List you should, of course, check if it already exists.
One somewhat awkward part of this is the Client OM will throw a ServerException when the List can't be found rather than just returning a null object.
List list = web.Lists.GetByTitle("CSOM List");
try
{
ctx.ExecuteQuery();
if(list != null)
{
list.DeleteObject();
ctx.ExecuteQuery();
}
}
catch
{
}
After the check has been made and the List deleted if necessary, you can create the List using the ListCreationInformation. Just as with the ListItemCreationInformation shown earlier, this objects applies information about the List that is to be created.
ListCreationInformation createInfo = new ListCreationInformation();
createInfo.Title = "CSOM List";
createInfo.QuickLaunchOption = QuickLaunchOptions.On;
createInfo.TemplateType = (int)ListTemplateType.GenericList;
list = web.Lists.Add(createInfo);
list.EnableFolderCreation = true;
list.Update();
After the List has been created you add the Fields. The XML supplied to the AddFieldAsXml method is the same as you would use in an Elements.xml to define Fields being added to your by a SharePoint project.
Field field = list.Fields.AddFieldAsXml("<Field Type='Text' DisplayName='SomeField'></Field>",
true, AddFieldOptions.DefaultValue);
field.DefaultValue = "Hello, World";
field.Update();
field = list.Fields.AddFieldAsXml(@"<Field Type='Choice' DisplayName='IsThisFun' Format='RadioButtons'>
<Default>Yes<</Default>
<CHOICES>
<CHOICE>Yes</CHOICE>
<CHOICE>No</CHOICE>
<</CHOICES>
</Field>",
true, AddFieldOptions.DefaultValue);
Now that the List and Fields have been created you add ListItems as in previous examples.
ListItemCreationInformation itemCreateInfo = new ListItemCreationInformation();
ListItem item = list.AddItem(itemCreateInfo);
item["Title"] = "One";
item.Update();
Adding ListItem with Folder
In the previous examples all ListItems have been added to the root folder of the list. However, there are cases when the ListItem should be placed in folder.
In this example I've left out checking of the folder exists since the List is
being created here also but you should be able to understand from the previous
examples how to make that check.
itemCreateInfo = new ListItemCreationInformation();
itemCreateInfo.LeafName = "New Folder";
itemCreateInfo.UnderlyingObjectType = FileSystemObjectType.Folder;
item = list.AddItem(itemCreateInfo);
item.Update();
Here I set the UnderlyingObjectType property to FileSystemObjectType.Folder and the LeafName property to the name of the folder. Afterward, I use the FolderUrl property
to the relative address of the folder that was created in this list before
adding the ListItem
itemCreateInfo = new ListItemCreationInformation();
itemCreateInfo.FolderUrl = "/Lists/CSOM List/New Folder";
item = list.AddItem(itemCreateInfo);
item["Title"] = "Three";
item.Update();
Conclusion
The SharePoint 2010 Client Object Model is a great improvement over previous
editions of SharePoint and opens SharePoint to wide array of applications and
possibilities. Just like with SharePoint itself the Client OM is vast with many
more features then I have time to cover here. Hopefully both of these articles have
given you a good background on what the SharePoint Client Object Model is, how
it works and a hint of the possibilities it presents.
History
Initial posting: 10/13/11