Обработка больших папок и списков

Дата последнего изменения: 9 марта 2015 г.

Применимо к: SharePoint Foundation 2010

В этой статье
Регулирование запросов к большим спискам
Работа с папками и списками
Удаление нескольких версий элемента списка

При увеличении размера папок и списков необходимо разработать настраиваемый код, который работает с ними для оптимизации производительности. В противном случае приложения будут работать медленно и могут вызвать тайм-ауты работы службы или страницы. Две основные области внимания при обработке больших папок и списков следующие:

  • Регулирование запросов, которое может вызвать непредвиденное изменение поведения кода со временем, когда сайт расширится и запросы начнут возвращать элементы, превышающие пороговую величину запросов.

  • Эффективное извлечение элементов из больших папок и списков.

Чтобы устранить обе проблемы, необходимо понять, как объектная модель взаимодействует с папками и списками.

Регулирование запросов к большим спискам

В Microsoft SharePoint Foundation 2010 и Microsoft SharePoint Server 2010 по умолчанию для запросов применяется пороговое значение в 5000 элементов. Весь пользовательский код, зависящий от наборов результатов запросов, которые могут превышать этот максимум, не будет работать ожидаемым образом. Запросы для списков, содержащих более 5000 элементов, включающих поля, не индексированные в условиях запросов, будут также завершаться ошибкой, так как эти запросы должны проверять все строки в списке. Следуйте приведенным ниже инструкциям, чтобы просмотреть и увеличить указанный предел или чтобы позволить объектной модели перезаписать этот предел:

Просмотр и увеличение порогового значения или разрешение объектной модели перезаписать пороговое значение

  1. На сайте Центр администрирования в разделе Управление приложениями щелкните Управление веб-приложениями.

  2. Щелкните Общие параметры, а затем выберите Регулирование ресурсов.

  3. Просмотрите и обновите пороговое значение или разрешите объектной модели перезаписать пороговое значение.

Работа с папками и списками

Следующие рекомендации по устранению проблем производительности при работе с большими папками и списками основаны на результатах тестирования, опубликованных в техническом документе Стива Пешки Работа с большими списками в Office SharePoint Server 2007 (Возможно, на английском языке). Эти рекомендации также применяются к Microsoft SharePoint Server 2010. Дополнительные сведения по использованию классов SPQuery и PortalSiteMapProvider, применимые, в частности, к SharePoint Server 2010, см. в разделе Написание эффективного кода в SharePoint Server.

При работе с папками и списками:

  • Не используйте SPList.Items.

    SPList.Items выделяет все элементы во всех вложенных папках, включая все поля в списке. Используйте следующие альтернативы в каждом случае.

    • Добавление элемента

      Вместо вызова SPList.Items.Add используйте SPList.AddItem.

    • Извлечение всех элементов в списке

      Вместо использования SPList.Items используйте SPList.GetItems(SPQuery query) . Примените фильтры, если возможно, и задайте только требуемые поля, чтобы сделать запрос более эффективным. Если список содержит более 2000 элементов, разбейте список на страницы с шагом не более 2000 элементов. Следующий пример кода показывает, как разбить на страницы большой список.

      Рекомендуемый прием программирования

      Извлечение элементов с помощью SPList.GetItems

      SPQuery query = new SPQuery();
      SPListItemCollection spListItems ; 
      string lastItemIdOnPage = null; // Page position.
      int itemCount = 2000
       
      while (itemCount == 2000)
      {
          // Include only the fields you will use.
          query.ViewFields = "<FieldRef Name=\"ID\"/><FieldRef Name=\"ContentTypeId\"/>";   
          query.RowLimit = 2000; // Only select the top 2000.
          // Include items in a subfolder (if necessary).
          query.ViewAttributes = "Scope=\"Recursive\"";
          StringBuilder sb = new StringBuilder();
          // To make the query order by ID and stop scanning the table, specify the OrderBy override attribute.
          sb.Append("<OrderBy Override=\"TRUE\"><FieldRef Name=\"ID\"/></OrderBy>");
          //.. Append more text as necessary ..
          query.Query = sb.ToString();
          // Get 2,000 more items.
       
          SPListItemCollectionPosition pos = new SPListItemCollectionPosition(lastItemIdOnPage);
          query.ListItemCollectionPosition = pos; //Page info.
          spListItems = spList.GetItems(query);
          lastItemIdOnPage = spListItems.ListItemCollectionPosition.PagingInfo;
          // Code to enumerate the spListItems.
          // If itemCount <2000, finish the enumeration.
          itemCount = spListItems.Count;
      
      }
      
      Dim query As New SPQuery()
      Dim spListItems As SPListItemCollection
      Dim lastItemIdOnPage As String = Nothing ' Page position.
      Dim itemCount As Integer = 2000
      
      Do While itemCount = 2000
          ' Include only the fields you will use.
          query.ViewFields = "<FieldRef Name=""ID""/><FieldRef Name=""ContentTypeId""/>"
          query.RowLimit = 2000 ' Only select the top 2000.
          ' Include items in a subfolder (if necessary).
          query.ViewAttributes = "Scope=""Recursive"""
          Dim sb As New StringBuilder()
          ' To make the query order by ID and stop scanning the table, specify the OrderBy override attribute.
          sb.Append("<OrderBy Override=""TRUE""><FieldRef Name=""ID""/></OrderBy>")
          '.. Append more text as necessary ..
          query.Query = sb.ToString()
          ' Get 2,000 more items.
      
          Dim pos As New SPListItemCollectionPosition(lastItemIdOnPage)
          query.ListItemCollectionPosition = pos 'Page info.
          spListItems = spList.GetItems(query)
          lastItemIdOnPage = spListItems.ListItemCollectionPosition.PagingInfo
          ' Code to enumerate the spListItems.
          ' If itemCount <2000, finish the enumeration.
          itemCount = spListItems.Count
      Loop
      

      Следующий пример показывает, как перечислить и разбить на страницы большой список.

      SPWeb oWebsite = SPContext.Current.Web;
      SPList oList = oWebsite.Lists["Announcements"];
      
      SPQuery oQuery = new SPQuery();
      oQuery.RowLimit = 10;
      int intIndex = 1;
      
      do
      {
          Response.Write("<BR>Page: " + intIndex + "<BR>");
          SPListItemCollection collListItems = oList.GetItems(oQuery);
      
          foreach (SPListItem oListItem in collListItems)
          {
              Response.Write(SPEncode.HtmlEncode(oListItem["Title"].ToString()) +"<BR>");
          }
      
          oQuery.ListItemCollectionPosition = collListItems.ListItemCollectionPosition;
          intIndex++;
      } while (oQuery.ListItemCollectionPosition != null);
      
       Dim oWebsite As SPWeb = SPContext.Current.Web
      Dim oList As SPList = oWebsite.Lists("Announcements")
      
      Dim oQuery As New SPQuery()
      oQuery.RowLimit = 10
      Dim intIndex As Integer = 1
      
      Do
          Response.Write("<BR>Page: " & intIndex & "<BR>")
          Dim collListItems As SPListItemCollection = oList.GetItems(oQuery)
      
          For Each oListItem As SPListItem In collListItems
              Response.Write(SPEncode.HtmlEncode(oListItem("Title").ToString()) & "<BR>")
          Next oListItem
      
          oQuery.ListItemCollectionPosition = collListItems.ListItemCollectionPosition
          intIndex += 1
      Loop While oQuery.ListItemCollectionPosition IsNot Nothing
      
    • Получение элементов по идентификатору

      Вместо использования SPList.Items.GetItemById используйте SPList.GetItemById(int id, string field1, params string[] fields). Укажите требуемые идентификатор элемента и поле.

  • Не перечисляйте все коллекции SPList.Items или SPFolder.Files.

    В левом столбце в таблице 1 приведены методы и свойства, которые при использовании перечисляют всю коллекцию SPList.Items и приводят к низкой производительности и плохому регулированию больших списков. Вместо них используйте лучше работающие альтернативы, перечисленные в правом столбце.

    Таблица 1. Альтернативы перечислению SPList.Items

    Плохо работающие методы и свойства

    Лучше работающие альтернативы

    SPList.Items.Count

    SPList.ItemCount

    SPList.Items.XmlDataSchema

    Создайте объект SPQuery, чтобы извлекать только требуемые элементы.

    SPList.Items.NumberOfFields

    Создайте объект SPQuery (указав ViewFields), чтобы извлекать только требуемые элементы.

    SPList.Items[System.Guid]

    SPList.GetItemByUniqueId(System.Guid)

    SPList.Items[System.Int32]

    SPList.GetItemById(System.Int32)

    SPList.Items.GetItemById(System.Int32)

    SPList.GetItemById(System.Int32)

    SPList.Items.ReorderItems(System.Boolean[],System.Int32[],System.Int32)

    Выполняйте страничный запрос с помощью SPQuery и пересортируйте элементы в каждой странице.

    SPList.Items.ListItemCollectionPosition

    ContentIterator.ProcessListItems(SPList, ContentIterator.ItemProcessor, ContentIterator.ItemProcessorErrorCallout) (только Microsoft SharePoint Server 2010)

    SPList.Items.ListItemCollectionPosition

    ContentIterator.ProcessListItems(SPList, ContentIterator.ItemProcessor, ContentIterator.ItemProcessorErrorCallout) (только SharePoint Server 2010)

    ПримечаниеПримечание

    Использование свойства SPList.ItemCount является рекомендуемым способом получения числа элементов в списке. Однако в качестве побочного эффекта настройки этого свойства для увеличения производительности может быть случайный возврат свойством непредвиденных результатов. Например, если требуется точное число элементов, нужно использовать менее производительную функцию GetItems(SPQuery query), как показано в предыдущем примере кода.

  • По возможности получайте ссылки на список с помощью идентификатора GUID или URL-адреса списка в качестве ключа.

    Можно получить объект SPList из свойства SPWeb.Lists с помощью идентификатора GUID или отображаемого имени списка в качестве индексатора. Использование SPWeb.Lists[GUID] и SPWeb.GetList(strURL) всегда предпочтительнее, чем использование SPWeb.Lists[strDisplayName]. Использование идентификатора GUID предпочтительнее, так как он является уникальным, постоянным и требует только одного просмотра базы данных. Индексатор отображаемого имени получает имена всех списков сайта и затем проводит строковое сравнение с ними. Если вместо идентификатора GUID имеется URL-адрес, можно использовать метод GetList в SPWeb, чтобы просмотреть идентификатор GUID списка в базе данных контента перед получением списка.

  • Не перечисляйте все коллекции SPFolder.Files.

    В левом столбце таблицы 2 приведены методы и свойства, которые расширяют коллекцию SPFolder.Files и приводят к низкой производительности и плохому регулированию больших списков. Вместо них используйте лучше работающие альтернативы, приведенные в правом столбце.

    Таблица 2. Альтернативы SPFolders.Files

    Плохо работающие методы и свойства

    Лучше работающие альтернативы

    SPFolder.Files.Count

    SPFolder.ItemCount

    SPFolder.Files.GetEnumerator()

    ContentIterator.ProcessFilesInFolder(SPFolder, System.Boolean, ContentIterator.FileProcessor, ContentIterator.FileProcessorErrorCallout) (только SharePoint Server 2010)

    SPFolder.Files[System.String]

    ContentIterator.GetFileInFolder(SPFolder, System.String) В качестве альтернативы, SPFolder.ParentWeb.GetFile(SPUrlUtility.CombineUrl(SPFolder.Url, System.String) (только SharePoint Server 2010)

    SPFolder.Files[System.Int32]

    Не используйте. Пройдите по ContentIterator.ProcessFilesInFolder и посчитайте элементы во время итерации. (Только SharePoint Server 2010)

Удаление нескольких версий элемента списка

При удалении нескольких версий элемента списка используйте метод DeleteByID(). Не используйте метод Delete(). Если удалять каждый объект SPListItemVersion из объекта SPListItemVersionCollection возникнут проблемы с производительностью. Рекомендуется создать массив со свойствами идентификаторов каждой версии и затем удалить все версии с помощью метода SPFileVersionCollection.DeleteByID. В следующих примерах кода показаны оба способа, рекомендуемый и нерекомендуемый, для удаления всех версий первого элемента настраиваемого списка.

Пример неправильного кода

Удаление всех объектов SPListItemVersion

SPSite site = new SPSite("site url");
SPWeb web = site.OpenWeb();
SPList list = web.Lists["custom list name"];
SPListItem item = list.GetItemById(1); 
SPListItemVersionCollection vCollection = item.Versions;
ArrayList idList = new ArrayList();
foreach(SPListItemVersion ver in vCollection)
{
  idList.Add(ver.VersionId);
}
foreach(int verID in idList)
{
  SPListItemVersion version = vCollection.GetVersionFromID(verID); 
try
{
  version.Delete();
}
catch (Exception ex)
{
  MessageBox.Show(ex.Message);  
}
}

Практическая рекомендация по кодированию

Удаление всех версий элемента списка с помощью метода SPFileVersionCollection.DeleteByID

SPSite site = new SPSite("site url");
SPWeb web = site.OpenWeb();
SPList list = web.Lists["custom list name"];
SPListItem item = list.GetItemById(1);
SPFile file = web.GetFile(item.Url);
SPFileVersionCollection collection = file.Versions;
ArrayList idList = new ArrayList();
foreach (SPFileVersion ver in collection)
{
  idList.Add(ver.ID);
}
foreach (int verID in idList)
{
try
{
  collection.DeleteByID(verID);
}
catch (Exception ex)
{
  MessageBox.Show(ex.Message);  
}
}

При удалении версий элемента в библиотеке документов можно использовать аналогичный подход, извлекая свойство SPListItem.File.Versions, как показано в следующем примере кода.

Практическая рекомендация по кодированию

Удаление всех версий элемента списка в библиотеке документов с помощью метода SPFileVersionCollection.DeleteByID

SPSite site = new SPSite("site url");
SPWeb web = site.OpenWeb();
SPList list = web.Lists["custom list name"];

SPFile file = list.RootFolder.Files[0];
SPFileVersionCollection collection = file.Versions;
ArrayList idList = new ArrayList();
foreach (SPFileVersion ver in collection)
{
  idList.Add(ver.ID);
}
foreach (int verID in idList)
{
try
{
  collection.DeleteByID(verID);
}
catch (Exception ex)
{
  MessageBox.Show(ex.Message);  
}
}