5

I am trying to optimize some code that calls the GetWOPIFrameUrl method for list items that were retrieved using a CAML query. The original code executed the CAML query to get the list of items, then later on made a separate GetWOPIFrameUrl call for each list item. I am trying to combine these to reduce the number of calls as much as possible.

My first attempt was to add the GetWOPIFrameUrl call to the original query like this:

var listItems = list.GetItems(query);
context.Load(listItems,
    items => items.Include(
        // ... other fields
        item => item.File.GetWOPIFrameUrl(SPWOPIFrameAction.Edit)));

But this generates a "query expression is not supported" error.

The second approach I am trying is to create a second query for all of the WOPI URLs and execute it immediately after the first query. This approach works, but it ends up returning duplicate data, which I'd prefer to avoid.

The code for that looks like this:

// The list was queried elsewhere
using (var context = list.Context)
using (authContext.InitiateContext(context))
{
    var query = new CamlQuery();
    query.ViewXml = "..."; // CAML query

    var listItems = list.GetItems(query);
    context.Load(listItems,
        items => items.Include(
            item => item.Id,
            item => item.FileSystemObjectType,
            item => item.DisplayName,
            item => item["Modified"],
            item => item.File.ServerRelativeUrl));
    context.ExecuteQuery();

    var wopiUrls = new Dictionary<ListItem, ClientResult<string>>();
    // The useWopi flag is set when WOPI URLs are desired
    if (useWopi)
    {
        foreach (var listItem in listItems)
        {
            wopiUrls[listItem] = listItem.GetWOPIFrameUrl(SPWOPIFrameAction.Edit);
            context.Load(listItem, item => item.Id);
        }
        // This query includes Id, FileSystemObjectType, DisplayName, and Modified
        context.ExecuteQuery();
    }

    // Do things with listItems and wopiUrls
}

As noted in the comment, the second query is including the FileSystemObjectType, DisplayName, and Modified columns, which are not needed in the second query. It appears that they are inheriting those columns from the original request. I assume that this is happening because I'm reusing the instances returned from the original request to make the second one.

Ideally I'd like to get the single-query approach working. If this is not possible, I'd like to get the second query working without having it return the extra columns.

1 Answer 1

2

Single query approach

It seems there is no way to combine ListItem.GetWOPIFrameUrl method and ClientRuntimeContext.Load method.

I would propose the following solution. Let's introduce the below method that returns the same result as as ListItem.GetWOPIFrameUrl method but does not require request to the server, instead it generates url based on List Item properties:

public static string GetListItemWOPIUrl(string webUrl, Guid uniqueId, SPWOPIFrameAction action)
{
    return string.Format("{0}/_layouts/15/WopiFrame.aspx?sourcedoc={1}&action={2}", webUrl, uniqueId.ToString("B"), action.ToString().ToLower());
}

In that case the example that requires a single request to server could look like this:

using (var ctx = GetContext(webUri, userName, password)) 
{
    var list = ctx.Web.Lists.GetByTitle(listTitle);
    var query = new CamlQuery();

    var listItems = list.GetItems(query);
    ctx.Load(listItems,
    items => items.Include(
                    item => item.Id,
                    item => item.FileSystemObjectType,
                    item => item.DisplayName,
                    item => item["Modified"],
                    item => item.File.ServerRelativeUrl,
                    item => item.File.UniqueId)); //File.UniqueId is required to generate WOPI url
    ctx.ExecuteQuery();

    var wopiUrls = new Dictionary<ListItem, string>();
    foreach (var item in listItems)
    {
          wopiUrls[item] = GetListItemWOPIUrl(webUri.ToString(), item.File.UniqueId, SPWOPIFrameAction.View);
    }
}

Note: File.UniqueId property along with web url is required to generate WOPIFrameUrl

2
  • Thanks for the reply. It is not a bad idea, but we had a couple concerns with generating the URLs in our software. First, it would make this code sensitive to the SharePoint version (if the format of the URL were to change). Second, we do not want to present the Edit option if it is not available, so we need to know which files actually have it available. I've been researching the WOPI discovery mechanism but so far have not been able to determine if it will be reliable enough for this purpose (particularly for SharePoint Online instances).
    – Jack A.
    Oct 22, 2015 at 12:02
  • @JackA.'s concerns are very valid. The whole reason we want to use let the object model return the WOPI URL is because it has a different format from system to system. -- On one system it needs an extra set of curly braces and on the other it doesn't. There may be other nuances as well. (One of our systems is O365 SharePoint, and the other is SP2013 on-prem.) -- But from what we've seen, if you generate the URL yourself, you are tying yourself to a specific system -- you might as well hardcode the webUrl parameter with this approach. Jul 16, 2016 at 0:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.