SharePoint CSOM – Update document and list item property together

csomsharepointsharepoint-online

I have the requirement to process a document list item when it is changed by a remote event receiver. The remote event receiver is wired into the itemupdated event. There is an obvious issue with this that I will get stuck in an infintite loop if I update an item in the itemupdated event.

To avoid this infinite loop, and in the absence of something like EventFiringEnabled that I would have used with old school SharePoint development, I thought about adding a "Processed" flag on the list item to indicate that the document has been processed.

The logic flow then looks like this.

  • Document is added to library – nothing special here
  • Metadata is filled out on list item – ItemUpdated event fires
  • Remote event receiver springs into life. Checks the "Processed" flag. If true, do nothing, if false, carry on. Using the metadata on the document, it "Processes" it. Document is updated and list item "Processed" flag is set to true.

The trouble I'm having is updating the document and the list item in a single transaction.

My SaveFile method looks like this:

    private void SaveFile(List list, string url, MemoryStream memoryStream)
    {
        var newFile = new FileCreationInformation
        {
            ContentStream = memoryStream,
            Url = url,
            Overwrite = true
        };

        File updatedFile = list.RootFolder.Files.Add(newFile);
        ListItem item = updatedFile.ListItemAllFields;
        item["Processed"] = true;
        item.Update();

        _clientContext.ExecuteQuery();
    }

But this manifests itself as two updates. The file is updated, and then the list item is. Does anyone know if I can force this into a single update?

Best Answer

In fact, in the specified example only a single request is submitted to the server (when ClientContext.ExecuteQuery method is invoked). It could be verified using network monitoring tool like Fiddler.

Here is a slightly modified version of original example with comments:

private static void SaveFile(List list, string url, Stream contentStream,IDictionary<string,object> itemProperties)
{
    var ctx = list.Context;
    var fileInfo = new FileCreationInformation
    {
        ContentStream = contentStream,
        Url = url,
        Overwrite = true
    };
    var file = list.RootFolder.Files.Add(fileInfo); //1. prepare to add file
    var item = file.ListItemAllFields;
    foreach (var p in itemProperties)
       item[p.Key] = p.Value;
    item.Update();  //2. prepare list item to update
    ctx.ExecuteQuery();  //3. submit request to the server that includes adding the file and updading the associated list item properties. 
}

Update

Using ListItem.ValidateUpdateListItem method that has the following signature:

public IList<ListItemFormUpdateValue> ValidateUpdateListItem(
    IList<ListItemFormUpdateValue> formValues,
    bool bNewDocumentUpdate,
    string checkInComment
)

this method is similar to the UpdateOverwriteVersion method available on the server API. ValidateUpdateListItem sets the metdata and if the bNewDocumentUpdate argument is set to true will call the UpdateOverwriteVersion method which will update without incrementing the version

private static void SaveFile(List list, string url, Stream contentStream,List<ListItemFormUpdateValue> itemProperties)
{
        var ctx = list.Context;
        var fileInfo = new FileCreationInformation
        {
            ContentStream = contentStream,
            Url = url,
            Overwrite = true
        };

        var file = list.RootFolder.Files.Add(fileInfo);
        var item = file.ListItemAllFields;
        item.ValidateUpdateListItem(itemProperties, true, string.Empty);
        ctx.ExecuteQuery();
}

Usage:

var list = ctx.Web.Lists.GetByTitle(listTitle);
using (var stream = System.IO.File.OpenRead(filePath))
{
    var itemProperties = new List<ListItemFormUpdateValue>();
    itemProperties.Add(new ListItemFormUpdateValue() { FieldName = "Processed", FieldValue = "true" });
    SaveFile(list, pageUrl, stream,itemProperties);    
}
Related Topic