如何使用 CSOM 在 Sharepoint 2013 中递归更改 ListItems 的值

How to change values of ListItems recursively in Sharepoint 2013 using CSOM

我的情况

你好, 我的客户有一个 Sharepoint Server 2013 on Premise。他想在多个文件上写入元数据。更具体地说,他想选择一个文件夹,然后应用程序应该在该文件夹中的每个 file/folder 的列中写入相同的值。

我已经做了什么

我写了一个 CAML 查询 returns 所有 files/folder 在所选文件夹下。然后我写下每个项目的值并更新它。

我想做什么

我想编写一个应用程序,我的客户可以在其中选择一个文件夹。之后,软件应更新该文件夹下的所有文件。 我想使用 C# 和 CSOM 编写此应用程序。

我的问题

这很慢。每个文件夹有数千个文件,仅完成一个文件夹就需要数小时。

我的问题

有没有办法加快速度?
CAML 查询也可以更新值吗?
做我想做的事情的推荐方法是什么?
有没有其他方法可以实现我的客户想要的?

感谢您的帮助。

您可以利用称为 Request Batching 的功能,这反过来又可以通过 最大限度地减少客户端和服务器之间传递的消息数量来显着影响性能 .

来自documentation

The CSOM programming model is built around request batching. When you work with the CSOM, you can perform a series of data operations on the ClientContext object. These operations are submitted to the server in a single request when you call the ClientContext.BeginExecuteQuery method.

为了比较,让我们演示两种更新多个列表项的方法:

标准方式:

foreach (var item in items)
{
   item["LastReviewed"] = DateTime.Now;
   item.Update();
   ctx.ExecuteQuery();
}

批量更新:

foreach (var item in items)
{
   item["LastReviewed"] = DateTime.Now;
   item.Update();
}
ctx.ExecuteQuery();

Note: in the second approach only a single batched request is submitted to the server

完整示例

var listTitle = "Documents";
var folderUrl = "Archive/2013";

using (var ctx = GetContext(url, userName, password))
{
     //1. Get list items operation
     var list = ctx.Web.Lists.GetByTitle(listTitle);
     var items = list.GetListItems(folderUrl);
     ctx.Load(items);
     ctx.ExecuteQuery();


     //2. Update list items operation
     var watch = Stopwatch.StartNew();
     foreach (var item in items)
     {
           if(Convert.ToInt32(item["FSObjType"]) == 1) //skip folders
               continue;

           item["LastReviewed"] = DateTime.Now;
           item.Update();
           //ctx.ExecuteQuery();
           Console.WriteLine("{0} has been updated", item["FileRef"]);
     }
     ctx.ExecuteQuery();  //execute batched request
     watch.Stop();
     Console.WriteLine("Update operation completed: {0}", watch.ElapsedMilliseconds); 

     Console.ReadLine();
}

其中 GetListItems 是获取位于特定文件夹下的列表项的扩展方法:

using System.Linq;
using Microsoft.SharePoint.Client;

namespace SharePoint.Client.Extensions
{
    public static class ListCollectionExtensions
    {

        /// <summary>
        /// Get list items located under specific folder 
        /// </summary>
        /// <param name="list"></param>
        /// <param name="relativeFolderUrl"></param>
        /// <returns></returns>
        public static ListItemCollection GetListItems(this List list, string relativeFolderUrl)
        {
            var ctx = list.Context;
            if (!list.IsPropertyAvailable("RootFolder"))
            {
                ctx.Load(list.RootFolder, f => f.ServerRelativeUrl);
                ctx.ExecuteQuery();
            }
            var folderUrl = list.RootFolder.ServerRelativeUrl + "/" + relativeFolderUrl;
            var qry = CamlQuery.CreateAllItemsQuery();
            qry.FolderServerRelativeUrl = folderUrl;
            var items = list.GetItems(qry);
            return items;
        }
    }
}