В этом посте я хочу рассказать о
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 делает много
инфраструктурной работы за вас. Это сэкономит ваше время, позволив
сконцентрироваться на непосредственной бизнес-задачей. И на последок хороший пример из MSDN
тут
Взято из: http://www.aviw.net/2012/08/sharepoint-contentiterator.html