Wednesday, 23 March 2011

Using the SharePoint 2010 Silverlight Client Object Model to Update Documents

Earlier this month, I blogged on how you can use the Silverlight client object model to retrieve files from a SharePoint document library. This time, let's take a look at how you can add or update files in a document library from Silverlight.

Just like the process for retrieving files, the process for adding or updating files differs between managed .NET clients and Silverlight clients. The Silverlight client object model does not support the File.SaveBinaryDirect method, so the recommended approach for managed clients is not available to us. From a Silverlight client, the high-level process is as follows:

  • Convert the contents for your new file to a byte array
  • Create a FileCreationInformation instance to represent the new file
  • Add the file to a folder in a document library

The code should resemble the following:

ClientContext context = ClientContext.Current;
String fileContents = "This is the contents of my file";
String fileUrl = String.Format(@"{0}/{1}/{2}/{3}",
   new String[]
      {context.Url, libraryPath, folderName, filename});

//Convert the file contents to a byte array
System.Text.UTF8Encoding encoding =
   new System.Text.UTF8Encoding();
Byte[] fileBytes = encoding.GetBytes(fileContents);

//Create an object to represent the file
FileCreationInformation fileCreationInfo =
   new FileCreationInformation();
fileCreationInfo.Url = fileUrl;
fileCreationInfo.Content = fileBytes;
//Overwrite the file if it exists, create if it doesn't
fileCreationInfo.Overwrite = true;

//Add the file to a library
List targetList =
   context.Web.Lists.GetByTitle("My Document Library");
targetList.RootFolder.Files.Add(fileCreationInfo);
targetList.Update();
context.ExecuteQueryAsync(SaveFileSucceeded,
   SaveFileFailed);

And that's how you save a file to a SharePoint document library. You don't need to do anything specific in the callback methods, other than check for errors or report success back to the user. Note that you don't need to add your file to a specific folder in the document library—you can simply add it to the root folder, and SharePoint will use the URL you provided to put it in the right place. Unlike the server-side object model, the Silverlight client object model doesn't expose a collection of files on the Web object.

One limitation of this approach is that it doesn't allow you to specify a content type or provide any metadata for the file. I plan to look a little deeper into this in a later post.

Monday, 21 March 2011

SharePoint 2010 Query Thresholds Bite You When You Least Expect It

In a previous post, Understanding List Query Throttling Limits in SharePoint 2010, I talked about how SharePoint 2010 applies query throttling to list queries and how you can work around the query thresholds. In this post I just wanted to add how list query thresholds can cause errors when you least expect it.

Today I was trying to export a SharePoint team site as a WSP. The Save site as template operation kept failing with an unexpected error. I took a look at the event logs, and found the following exception message:

Error exporting the list named "BigList" at the URL: Lists/BigList

"Fine", I thought to myself. I'm not particularly interested in that list, I'll simply delete it. I tried to delete the list and got hit with another runtime error. This time the event logs were more helpful:

Exception type: SPQueryThrottledException
Exception message: The attempted operation is prohibited because it exceeds the list view threshold enforced by the administrator.

Now things were starting to make more sense. The list I was trying to export, and then trying to delete, contains 10,000 items. Even though I'm not explicitly trying to retrieve all 10,000 items, both the export operation and the delete operation will hit all 10,000 rows in the database. The list view threshold is kicking in and blocking the operation.

"Fine", I thought to myself again. I'll grant myself full control permissions in the Web application user policy, thereby giving myself the higher auditors and administrators threshold for list operations. Wrong again. As I pointed out in my earlier post, the higher threshold only applies to programmatic queries in which you explicitly invoke the object model override. I was still unable to delete the list through the UI.

So how did I finally get rid of the large and unwanted list? I remembered that local server administrators are exempted from list view thresholds by default. I opened up a PowerShell window, using the local server administrator account, and ran a few cmdlets to delete the list once and for all.

$site = get-spsite http://mysiteurl
$web = $site.rootweb
$list = $web.lists["BigList"]
$list.delete()
$web.dispose()
$site.dispose()

And the list was gone. If you plan on creating large lists on your SharePoint 2010 sites, it's worth bearing in mind that they're not quite so straightforward to export, move or get rid of once you're finished with them.

Thursday, 17 March 2011

Using the SharePoint 2010 Silverlight Client Object Model to Retrieve Documents

This week I've been working on migrating a Silverlight application to SharePoint 2010. The application in question uses some fairly complex XML files as a data source, and currently relies on a custom Web service to retrieve and update these files. We want to modify the application to retrieve the XML files from a SharePoint 2010 document library. MSDN provides a good article on how to use the managed .NET client object model for SharePoint 2010 to retrieve and update documents in a SharePoint document library. However, this scenario becomes a little more challenging from a Silverlight client, as some of the required classes are unavailable in the Silverlight version of the client object model.

When you work with the managed client object model, the recommended approach for retrieving the contents of a file is to call the synchronous File.OpenBinaryDirect method. This returns a FileInformation instance that exposes the contents of the file as a stream. However, the FileInformation class is not included in the Silverlight client object model. Instead, the Silverlight client object model includes an alternative, asynchronous version of the File.OpenBinaryDirect method. This returns null, but exposes the contents of the file as a stream through the event arguments in a callback method.

Let's take a look at the code. Suppose we want to retrieve both the metadata for the file and the contents of the file.

ClientContext context = ClientContext.Current;
List targetList =
context.Web.Lists.GetByTitle("My Document Library");
CamlQuery query = new CamlQuery();
query.ViewXml =
   @"<View Scope='RecursiveAll'>
      <Query>
         <Where>
            <Eq>
               <FieldRef Name='FileLeafRef' />
               <Value Type='Text'>input.xml</Value>
            </Eq>
         </Where>
      </Query>
   </View>";

ListItemCollection targetListItems =
   targetList.GetItems(query);
context.Load(targetListItems);
context.ExecuteQuery();

We can now retrieve document metadata from the list item. For example, we could use the following code to establish when the document was created.

if(targetListItems.Count == 1)
{
   ListItem item = targetListItems[0];
   DateTime createdDate =
      Convert.ToDateTime(item["Created_x0020_Date"]);
}

To get the contents of the file, we use the Microsoft.SharePoint.Client.File.OpenBinaryDirect method and specify callback methods:

String serverRelativeUrl =
   @"/sitename/libraryname/foldername/input.xml";
File.OpenBinaryDirect(context, serverRelativeUrl,
   OnOpenSucceeded, OnOpenFailed);

In the callback method, we can read the contents of the file from the stream and do something useful with it.


private void OnOpenSucceeded(object sender, OpenBinarySucceededEventArgs args)
{
   StreamReader strReader = new StreamReader(args.Stream);
   String fileContents = strReader.ReadToEnd();
   strReader.Close();

   //Do something with the file contents
}

In a nutshell, that's how you retrieve SharePoint 2010 documents from a Silverlight client. Note that I used the synchronous ExecuteQuery method, rather than the asynchronous ExecuteQueryAsync method, to send my queries to the server. Silverlight will not allow you to block the UI thread, so if you want to use this approach you need to run your code on a background thread (for example, by using ThreadPool.QueueUserWorkItem to invoke your logic). You might find this approach preferable if you need to send multiple queries to the server—otherwise you can end up with a tangled web of nested callback methods.

Next time, I'll take a look at creating, updating, and deleting documents from a Silverlight client.

Monday, 7 March 2011

Where Are the SharePoint Client Assemblies?

If you're reading this post, you probably know that SharePoint 2010 provides client-side APIs for Silverlight apps, managed .NET clients, and JavaScript code. However, the assemblies you need in order to start developing SharePoint client apps can be elusive at first. In particular, the Silverlight assemblies aren't where you might expect. All the assemblies and JavaScript libraries that you need for client-side development are deployed to folders beneath the SharePoint root when you install SharePoint 2010. For the record, here's where you can find them.

  • If you’re developing a managed .NET client application—for example, a WPF application—you need to add references to Microsoft.SharePoint.Client.dll and Microsoft.SharePoint.Client.Runtime.dll. You can find them in the 14\ISAPI folder on your SharePoint server.

  • If you’re developing a Silverlight application, you need to add references to Microsoft.SharePoint.Client.Silverlight.dll and Microsoft.SharePoint.Client.Silverlight.Runtime.dll. You can find them in the 14\TEMPLATE\LAYOUTS\ClientBin folder on your SharePoint server.

  • If you’re writing JavaScript code, you can find SP.js and all the other SharePoint JavaScript libraries in the 14\TEMPLATE\LAYOUTS folder.

I hope this saves someone the trouble of trawling the SharePoint root looking for assemblies!

Update 10th March 2011: I keep reading that the Silverlight client assemblies are in the 14\ISAPI\ClientBin folder. This folder doesn't exist in any of my installations.