Должен ли я использовать один или несколько ClientContext для обновления очень большого списка?

#c# #sharepoint-online #listitem #csom

#c# #sharepoint-online #listitem #csom

Вопрос:

Мне нужно обновить большой список SharePoint, около 10 000 элементов или более. Каждый элемент списка содержит 4 столбца, которые необходимо обновить. Я не знаю, какой наилучший подход для этого сценария:

  1. Используйте 1 ClientContext, извлеките ListItemCollection в пакете из n строк, выполните цикл по каждому ListItem и обновите его столбцы перед следующим пакетом. Или
  2. Извлеките список ListItemCollectionPosition в пакете из n строк, выполните цикл по каждому ListItemCollectionPosition, создайте новый ClientContext, извлеките ListItemCollection и затем обновите.

Способ 1

 using (ClientContext ctx = new ClientContext(url))
{
   ctx.Credentials = new SharePointOnlineCredentials(username,password);
   List list = ctx.Web.Lists.GetByTitle("mylist");
   ctx.Load(list);
   ctx.ExecuteQuery();
   ListItemCollectionPosition pos = null;
   CamlQuery camlQuery = new CamlQuery
   {
       ViewXml = "<View Scope='Recursive'><RowLimit>100</RowLimit></View>"
   };
   do           
   {
        if (pos != null)
        {
            camlQuery.ListItemCollectionPosition = pos;
        }
        ListItemCollection listItemCollection = list.GetItems(camlQuery);
        ctx.Load(listItemCollection);
        ctx.ExecuteQuery();
        pos = listItemCollection.ListItemCollectionPosition;
        foreach(ListItem item in listItemCollection)
        {
            item["col1"] = "abc";
            item["col2"] = "def";
            item["col3"] = "ghi";
            item["col4"] = "jkl";
            item.Update();
        }
        ctx.ExecuteQuery();
    } while (pos != null);
}
  

Способ 2

 private void UpdateList()
{
   using (ClientContext ctx = new ClientContext(url))
   {
       ctx.Credentials = new SharePointOnlineCredentials(username,password);
       List list = ctx.Web.Lists.GetByTitle("mylist");
       ctx.Load(list);
       ctx.ExecuteQuery();
       ListItemCollectionPosition pos = null;
       CamlQuery camlQuery = new CamlQuery
       {
           ViewXml = "<View Scope='Recursive'><RowLimit>100</RowLimit></View>"
       };
       List<ListItemCollectionPosition> positions = new List<ListItemCollectionPosition>();
       do           
       {
           if (pos != null)
           {
               camlQuery.ListItemCollectionPosition = pos;
           }
           ListItemCollection listItemCollection = list.GetItems(camlQuery);
           ctx.Load(listItemCollection);
           ctx.ExecuteQuery();
           pos = listItemCollection.ListItemCollectionPosition;
           positions.Add(pos);            
       } while (pos != null);
       List<Task> tasks = new List<Task>();
       foreach(var position in positions)
       {
           tasks.Add(UpdateItem(position));
       }
       Task.WaitAll(tasks.ToArray());
    }
}
private Task UpdateItem(ListItemCollectionPosition pos)
{
   using (ClientContext ctx = new ClientContext(url))
   {
      ctx.Credentials = new SharePointOnlineCredentials(username,password);
      List list = ctx.Web.Lists.GetByTitle("mylist");
      ctx.Load(list);
      ctx.ExecuteQuery();
      CamlQuery camlQuery = new CamlQuery
      {
          ViewXml = "<View Scope='Recursive'><RowLimit>100</RowLimit></View>"
      };
      camlQuery.ListItemCollectionPosition = pos;
      ListItemCollection listItemCollection = list.GetItems(camlQuery);
      ctx.Load(listItemCollection);
      ctx.ExecuteQuery();
      foreach(ListItem item in listItemCollection)
      {
           item["col1"] = "abc";
           item["col2"] = "def";
           item["col3"] = "ghi";
           item["col4"] = "jkl";
           item.Update();
      }
      return ctx.ExecuteQueryAsync();
    }
}
  

Ответ №1:

В способах 1 и 2 я бы установил ViewFields для ограничения объема передаваемых данных.

В методе 2 UpdateItem метод выполняется после того, positions как коллекция заполнена. Почему бы не обновлять элементы списка во время заполнения positions коллекции — Вы могли бы реализовать enumerator с атрибутом yield.

 public static IEnumerable<ListItemCollectionPosition> GetPositions()
{            
     do           
     {
       ...
       yield return pos;
       ...
     } while (pos != null);
}