※注意:以下に書くことはまだ、[SharePoint]WSS3.0の累積的な更新・・・(KB961755)のパッチ未適用の環境でのことです。
あるリストで同一フォルダにアイテムが約2100以上あると、SPList.Items(SPListItemCollection)のメンバ使用するだけでも例外になってしまう場合があります。→[SharePoint]リストにアイテムが約2100件以上あると・・・(続き:再現)
このとき通常のWeb画面でもアイテムの一覧画面で
<!-- #RENDER FAILED -->
と表示されてしまいます。(kb/958577/en,kb/958577/)
このときSPListItemCollectionのCountプロパティやforeachで暗黙的に使用されるGetEnumerator メソッドでも例外になってしまいます。 一度には取得・列挙できません。
そこで、小分けに、IDの範囲を指定して分割して、たとえば100件以内ずつ複数回のクエリに分けて、取得してIDを表示する以下のサンプルプログラムを作成しました。
下のPrintAllListItemIds1とPrintAllListItemIds2はどちらも指定したリスト(ドキュメントライブラリ)のすべてのアイテムのIDを出力して最後に合計件数を出力するメソッド(のはず)です。一見するとどちらも同じ結果が期待できそうです。
[SharePoint]リストにアイテムが約2100件以上あると・・・(続き:再現)のときに作成した、直下のルートフォルダに4024件アイテムが登録されているドキュメントライブラリ(バージョン管理設定、下書きアイテムを表示できるユーザー = 「アイテムを編集できるユーザー 」)があるので、実際に実行してみました。
PrintAllListItemIds1
… … …
Number of Items : 1022
PrintAllListItemIds2
… … …
Number of Items : 4023
PrintAllListItemIds1は何度やってもこうなってしまいます。これは明らかに誤った結果です。
PrintAllListItemIds2も1件違いますが、これは別の問題です。別のユーザーがドキュメントライブラリにアップロードしたばかりでカスタムフィールドを編集していない、バージョン0.1のチェックアウトファイルと思われます。このようなドキュメントアイテムはItemsやクエリ結果に出現しません。
どうやらSPWebオブジェクトをクエリの都度生成しなおさないと、クエリを使用するだけでオブジェクトが何かおかしくなってしまうようです。
using System;
using Microsoft.SharePoint;
namespace TestSPQuery
{
class Program
{
const string QUERY_BY_ID_RANGE = @"<Where>
<And>
<Geq>
<FieldRef Name = ""ID""/>
<Value Type = ""Counter"">{0}</Value>
</Geq>
<Lt>
<FieldRef Name = ""ID""/>
<Value Type = ""Counter"">{1}</Value>
</Lt>
</And>
</Where>";
const int MaxItemID = 4500;//取得する最大IDの目安
const int DEFAULT_ID_RANGE = 100;//1回のクエリ対象にするIDの範囲
/// <summary>
/// start<=ID<endというクエリ
/// </summary>
public static string GetIDRangeQery(int start, int end)
{
return string.Format(QUERY_BY_ID_RANGE, start, end);
}
static void Main(string[] args)
{
String scUrl = "(サイトコレクションURL)";
String siteName = "サイト名";
String listName = "リスト名";
//PrintAllListItemIds1(scUrl, siteName, listName, DEFAULT_ID_RANGE);
//PrintAllListItemIds2(scUrl, siteName, listName, DEFAULT_ID_RANGE);
}
/// <summary>
/// 指定したリスト(ドキュメントライブラリ)のすべてのアイテムのID表示する。
/// </summary>
/// <param name="scUrl">サイトコレクションURL</param>
/// <param name="siteName">サイト名</param>
/// <param name="listName">リスト名</param>
/// <param name="rangesize">1回のクエリ対象にするIDの範囲</param>
private static void PrintAllListItemIds1(String scUrl, String siteName, String listName, int rangesize)
{
using (SPSite site = new SPSite(scUrl))
{
using (SPWeb web = site.AllWebs[siteName])
{
SPList list = web.Lists[listName];
Console.WriteLine("List Title :{0}, ItemCount: {1} ", list.Title, list.ItemCount);
int count = 1;
for (int start = 0; start <= MaxItemID; start += rangesize)
{
SPQuery q = new SPQuery();
q.Query = GetIDRangeQery(start, start + rangesize);
Console.WriteLine(q.Query);
SPListItemCollection itemColl = list.GetItems(q);
foreach (SPListItem item in itemColl)
{
Console.WriteLine("No.:{0} ID: {1} ",count++, item.ID);
}
}
Console.WriteLine("Number of Items : {0}",count-1);
}
}
}
/// <summary>
/// 指定したリスト(ドキュメントライブラリ)のすべてのアイテムのID表示する。
/// </summary>
/// <param name="scUrl">サイトコレクションURL</param>
/// <param name="siteName">サイト名</param>
/// <param name="listName">リスト名</param>
/// <param name="rangesize">1回のクエリ対象にするIDの範囲</param>
private static void PrintAllListItemIds2(String scUrl, String siteName, String listName, int rangesize)
{
using (SPSite site = new SPSite(scUrl))
{
using (SPWeb web = site.AllWebs[siteName])
{
SPList list = web.Lists[listName];
Console.WriteLine("List Title :{0}, ItemCount: {1} ", list.Title, list.ItemCount);
}
int count = 1;
for (int start = 0; start <= MaxItemID; start += rangesize)
{
using (SPWeb web = site.AllWebs[siteName])//クエリの度にサイト(SPWebオブジェクト)を生成。
{
SPList list = web.Lists[listName];
SPQuery q = new SPQuery();
q.Query = GetIDRangeQery(start, start + rangesize);
Console.WriteLine(q.Query);
SPListItemCollection itemColl = list.GetItems(q);
foreach (SPListItem item in itemColl)
{
Console.WriteLine("No.:{0} ID: {1} ", count++, item.ID);
}
}
}
Console.WriteLine("Number of Items : {0}", count - 1);
}
}
}
}