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.

Comments

  1. Hi, Nice post. I used your coding but I'm getting an exception while calling the ExecuteQuery() function. This is the exception message "The method or property that is called may block the UI thread and it is not allowed. Please use background thread to invoke the method or property, for example, using System.Threading.ThreadPool.QueueUserWorkItem method to invoke the method or property."

    Can u pls clarify me on why I'm getting this as I did not do anything to update the UI.

    ReplyDelete
  2. Hi SC. Your code will be running on the UI thread unless you've explicitly implemented multithreading (e.g. by using ThreadPool.QueueUserWorkItem) or you've used an asynchronous callback. Events are handled on the same thread on which they were fired - so if a UI event triggers your logic, your logic will run on the UI thread. You need to either (a) use the ExecuteQueryAsync method, or (b) use the ThreadPool.QueueUserWorkItem method to execute your query on a background thread. Hope that helps.

    ReplyDelete
  3. Hi Jason,

    I need to do multiple queries to the server (I need to pull list data from 3 different lists on 3 different sub sites). I am having trouble getting the ThreadPool.QueueUserWorkItem working. Do you think you could post the entire MainPage.xaml code page?

    ReplyDelete
  4. Hi Michael,

    Apologies for the delay. Unfortunately there is too much code to post here, but if you drop me a line (jasonl at contentmaster dot com) I'll mail you a copy of the code file.

    ReplyDelete
  5. Hello. Thank you for article but I am not sure how to create ThreadPool.QueueUserWorkItem because I tested everything and still doesnt work this. So I use for uploading files async but this is not OK. I have send question to technet but without any good solution: http://social.technet.microsoft.com/Forums/en-US/sharepoint2010general/thread/51d0ec37-d97f-40ce-89f3-5efe55936ba8 Thank youo for your help.

    ReplyDelete
    Replies
    1. The patterns & practices guidance for SharePoint 2010 includes code examples that use ThreadPool.QueueUserWorkItem - it might be worth taking a look at http://msdn.microsoft.com/en-us/library/ff798388.aspx. It's a big topic, the example you want is towards the bottom of the page (2nd to last code example). Hope that helps.

      Delete

Post a Comment

Popular posts from this blog

Server-side activities have been updated

The target principal name is incorrect. Cannot generate SSPI context.

Custom Workflow Activity for Creating a SharePoint Site