M365関連 プログラム 技術

GraphAPIでリストアイテムを全て取得する

以前にGraphAPI経由でSharePoint上のリストアイテム取得について記載しましたが、それだと200件ずつしか取得できないので1回の呼び出しで全件取得できる方法について紹介します

関連記事
SharePointを操作する(GraphAPI版) - ナストンのまとめ
SharePointを操作する(GraphAPI版) - ナストンのまとめ

以前、トークン等を使用することなくSharePointの情報を取得する方法を記載しましたが、今回はGraphAPI経由で ...

関連記事へ

名称バージョン
C#.Net 9.0
Azure.Identity1.17.1
Microsoft.Graph5.100.0

ありがとうスタフロ

調べても全件取得について書いている記事が見つからなかったのですが、以下の stack overflow にGraphAPIで200件しかリストアイテム取得できないけど、どうすれば全件取得できる?といった質問への回答として記載があるのを見つけました

参考サイト
https://stackoverflow.com/questions/77152680/ms-graph-v5-0-how-to-get-all-sharepoint-list-items
https://stackoverflow.com/questions/77152680/ms-graph-v5-0-how-to-get-all-sharepoint-list-items

参考サイトへ

実装に落とし込む

では、実際に実装してみると意外と簡単に実装できました
これでリストアイテムが200件以上でも問題なくGraphAPIで取得できるようになります

// 列の取得などのクエリなども問題なくできる
var response = await _graphClient.Sites[_siteId].Lists[listDisplayName].Items.GetAsync((requestConfiguration) =>
{
    requestConfiguration.QueryParameters.Expand = new string[] { $"fields($select={fields})" };
});

var listItems = new List<ListItem>();
var pageIterator = PageIterator<ListItem, ListItemCollectionResponse>.CreatePageIterator(_graphClient, response, item =>
{
    listItems.Add(item);
    return true;
});

// 以下を実行することで変数.listItemsに対象リストのアイテムがすべて格納されます
await pageIterator.IterateAsync();

おまけ

基本的にリストアイテムを取得して列の値を取得しようとしても「item.Fields?.AdditionalData.~」を記載して列情報を取得する必要がありますが、いちいちそのような指定をして値を取得するのがメンドウだったので列の値格納用のクラスを用意してそれに値を当てはめてるようにしました

列格納クラスに値を設定する

以下を使用することで取得したリストアイテムの列情報をクラス情報として格納します
注意点として列名(内部列名)とクラスの変数名は一致する必要があります

// ListItemのリストを指定した型のリストに変換
private List<T> ConvertToType<T>(List<ListItem> listItems) where T : new()
{
    var result = new List<T>();
    var properties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
    var idProperty = properties.FirstOrDefault(p => p.Name.Equals("Id", StringComparison.OrdinalIgnoreCase));

    foreach (var item in listItems)
    {
        if (item.Fields?.AdditionalData == null)
            continue;

        var obj = new T();

        // IdプロパティがあればIdを設定
        if (idProperty != null && idProperty.CanWrite)
        {
            SetPropertyValue(obj, idProperty, item.Id);
        }

        foreach (var property in properties)
        {
            // Idプロパティは設定済みのためスキップ
            if (property.Name.Equals("Id", StringComparison.OrdinalIgnoreCase))
                continue;

            // プロパティ名と一致するフィールドを探す
            var fieldName = property.Name;
            if (item.Fields.AdditionalData.TryGetValue(fieldName, out var value))
            {
                SetPropertyValue(obj, property, value);
            }
        }
        result.Add(obj);
    }

    return result;
}

// プロパティに値を設定
private void SetPropertyValue<T>(T obj, PropertyInfo property, object? value)
{
    if (value == null)
        return;

    try
    {
        var targetType = Nullable.GetUnderlyingType(property.PropertyType) ?? property.PropertyType;

        // JsonElementの場合は適切な型に変換
        if (value is JsonElement jsonElement)
        {
            value = ConvertJsonElement(jsonElement, targetType);
        }

        if (value != null && targetType.IsAssignableFrom(value.GetType()))
        {
            property.SetValue(obj, value);
        }
        else if (value != null)
        {
            var convertedValue = Convert.ChangeType(value, targetType);
            property.SetValue(obj, convertedValue);
        }
    }
    catch
    {
        // 変換に失敗した場合は無視
    }
}

// JsonElementを指定した型に変換
private object? ConvertJsonElement(JsonElement element, Type targetType)
{
    return element.ValueKind switch
    {
        JsonValueKind.String => targetType == typeof(DateTime) || targetType == typeof(DateTime?)
            ? DateTime.Parse(element.GetString()!)
            : element.GetString(),
        JsonValueKind.Number when targetType == typeof(int) || targetType == typeof(int?) => element.GetInt32(),
        JsonValueKind.Number when targetType == typeof(long) || targetType == typeof(long?) => element.GetInt64(),
        JsonValueKind.Number when targetType == typeof(double) || targetType == typeof(double?) => element.GetDouble(),
        JsonValueKind.Number when targetType == typeof(decimal) || targetType == typeof(decimal?) => element.GetDecimal(),
        JsonValueKind.Number => element.GetDouble(),
        JsonValueKind.True => true,
        JsonValueKind.False => false,
        JsonValueKind.Null => null,
        _ => element.ToString()
    };
}

最後に

これでリストアイテムを全て取得できるようになりました。また、列の値をクラス格納するようになりました
いつも通りGitHubに今回紹介したものをアップしていますので参考にしてください

サンプルコード
BlogSampleCodeProjects/Retrieve_AllListItems_Graphapi at main · nasuton/BlogSampleCodeProjects · GitHub
BlogSampleCodeProjects/Retrieve_AllListItems_Graphapi at main · nasuton/BlogSampleCodeProjects · GitHub

Project for sample code used in the blog.(Blogで記載しているサンプルコード ...

GtiHubへ

会社紹介

私が所属しているアドバンスド・ソリューション株式会社(以下、ADS)は一緒に働く仲間を募集しています

会社概要
「技術」×「知恵」=顧客課題の解決・新しい価値の創造

この方程式の実現はADSが大切にしている考えで、技術を磨き続けるgeekさと、顧客を思うloveがあってこそ実現できる世界観だと思っています
この『love & geek』の精神さえあれば、得意不得意はno problem!
技術はピカイチだけど顧客折衝はちょっと苦手。OKです。技術はまだ未熟だけど顧客と知恵を出し合って要件定義するのは大好き。OKです
凸凹な社員の集まり、色んなカラーや柄の個性が集まっているからこそ、常に新しいソリューションが生まれています

ミッション
私たちは、テクノロジーを活用し、業務や事業の生産性向上と企業進化を支援します

ホームページ
アドバンスド・ソリューション株式会社|ADS Co., Ltd.
アドバンスド・ソリューション株式会社|ADS Co., Ltd.

Microsoft 365/SharePoint/Power Platform/Azure による DX コンサル・シス ...

サイトへ移動

PR

-M365関連, プログラム, 技術
-, ,