Использование класса ContentIterator в разработке для Sharepoint

В этом посте я хочу рассказать о ContentIterator — полезном классе, который вы можете использовать в повседневной разработке для Sharepoint. Он определен в сборке Microsoft.Office.Server.dll и помогает работать с такими общими задачами, как итерация по списку элементов списка Sharepoint (SPListItem), файлов (SPFile), веб сайтов (SPWeb) и др. Итерация по элементам и файлам реализована через SPQuery, что значит он имеет очень хорошую производительность (близкую к максимальной из того, что предлагает Sharepoint object model). ContentIterator – новый класс, который появился в Sharepoint 2010. Как сказано в документации к классу:

Sharepoint сервер предоставляет новый API, ContentIterator, для помощи при работе с большими списками, содержащими более 5000 элементов, позволяющий обойти throttling-предел и генерации SPQueryThrottleException.

Например, представим, что у нас есть ссылка на объект типа SPFolder, представляющий папку в Sharepoint списке, и мы хотим пройтись в цикле по всем элементам внутри этой папки. Для этого мы можем использовать метод ContentIterator.ProcessFilesInFolder:

SPFolder folder = web.GetFolder(...);
SPList doclib = web.Lists[...];
ContentIterator contentIterator = new ContentIterator();

bool isFound = false;
contentIterator.ProcessFilesInFolder(doclib, folder, false,
    f =>
        {
            // this is item iteration handler
            ...
        },
    (f, e) =>
        {
            // error handler
            ...
        });

Работа с ContentIterator основана на использовании делегатов. В параметрах метода мы передаем ссылку на родительскую библиотеку документов (либо список) и папку в ней, в которой нужно проитерировать элементы. Третий параметр указывает, нужно ли делать обход рекурсивным если внутри нашей папки есть подпапки. Четвертый параметр – это делегат, который вызывается из ContentIterator-а и который получает непосредственно каждый файл (объект типа SPFile). Последний параметр – тоже делегат, представляющий обработчик ошибок.

Давайте посмотрим как реализован этот метод. Внутри он использует другой метод ContentIterator.ProcessListItems:

public void ProcessListItems(SPList list, string strQuery, uint rowLimit,
    bool fRecursive, SPFolder folder, ItemsProcessor itemsProcessor,
    ItemsProcessorErrorCallout errorCallout)
{
    ...
    SPQuery query = new SPQuery();
    if (!string.IsNullOrEmpty(strQuery))
    {
        query.Query = strQuery;
    }
    query.RowLimit = rowLimit;
    if (folder != null)
    {
        query.Folder = folder;
    }
    if (fRecursive)
    {
        query.ViewAttributes = "Scope=\"RecursiveAll\"";
    }
    this.ProcessListItems(list, query, itemsProcessor, errorCallout);
}

Для начала создается объект SPQuery. Непосредственный CAML-запрос формируется выше по стеку:

public static string ItemEnumerationOrderByPath
{
    get
    {
        return "<OrderBy Override='TRUE'><FieldRef Name='FileDirRef' /><FieldRef Name='FileLeafRef' /></OrderBy>";
    }
}

И наиболее интересная реализация перегруженного метода ProcessListItems:

public void ProcessListItems(SPList list, SPQuery query, ItemsProcessor itemsProcessor,
    ItemsProcessorErrorCallout errorCallout)
{
    string str2;
    SPListItemCollection items;
    ...
    if (!list.get_HasExternalDataSource() && (list.ItemCount == 0))
    {
        return;
    }
    if (list.get_HasExternalDataSource() && (query.RowLimit == 0))
    {
        query.RowLimit = 0x7fffffff;
    }
    else if ((query.RowLimit == 0) || (query.RowLimit == 0x7fffffff))
    {
        query.RowLimit = string.IsNullOrEmpty(query.ViewFields) ? 200 : 0x7d0;
    }
    if (!list.get_HasExternalDataSource() && this.StrictQuerySemantics)
    {
        query.set_QueryThrottleMode(2);
    }
    string strListId = list.ID.ToString("B");
    this.ResumeProcessListItemsBatch(strListId, out str2);
    if (!string.IsNullOrEmpty(str2))
    {
        query.ListItemCollectionPosition = new SPListItemCollectionPosition(str2);
    }
    int batchNo = 0;
Label_012B:
    items = list.GetItems(query);
    int count = items.Count;
    batchNo++;
    try
    {
        itemsProcessor(items);
        this.OnProcessedListItemsBatch(strListId, items, batchNo, count);
    }
    catch (Exception exception)
    {
        if ((errorCallout == null) || errorCallout(items, exception))
        {
            throw;
        }
    }
    if (!this.ShouldCancel(IterationGranularity.Item))
    {
        query.ListItemCollectionPosition = items.ListItemCollectionPosition;
        if (query.ListItemCollectionPosition != null)
        {
            goto Label_012B;
        }
    }
}

Внутри метода проверяется свойство RowLimit и, если оно не установлено, устанавливается в значения по умолчанию (в 200 или 2000 в зависимости от того, установлено ли свойство ViewFields или нет). Затем свойство SPQuery.QueryThrottleMode устанавливается в Strict. Документация говорит про это следующее:

Throttling запросов будет определяться числом элементов, а также полями Lookup, Person/Group и Workflow, вне зависимости от прав пользователей.

Также используется SPQuery.ListItemCollectionPosition для выборки элементов за один запрос (число элементов, возвращаемых за один запрос определяется свойством RowLimit).

Как видно из этих примеров ContentIterator делает много инфраструктурной работы за вас. Это сэкономит ваше время, позволив сконцентрироваться на непосредственной бизнес-задачей.

Реклама

Об авторе sadomovalex

Старший инженер, team lead, консультант. Работаю в стеке .Net. Последние несколько лет занимаюсь разработкой enterprise приложений под Sharepoint, чему и будет в основном посвящена тематика этого блога. Также активно использую и интересуюсь ASP.Net MVC, DDD, TDD, Agile. Активно участвую в жизни многих профессиональных сообществ, SPb .Net UG, SPb ALT.Net, rsdn, Finland SP UG и др.
Запись опубликована в рубрике CAML, Sharepoint. Добавьте в закладки постоянную ссылку.

Один комментарий на «Использование класса ContentIterator в разработке для Sharepoint»

  1. Anatoly Mironov:

    Спасибо за интересное объяснение и примеры о ContentEditor.

Добавить комментарий

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход / Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход / Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход / Изменить )

Google+ photo

Для комментария используется ваша учётная запись Google+. Выход / Изменить )

Connecting to %s