PageIterator 作为 GetAsync() 的通用扩展方法

问题描述 投票:0回答:1

图形 API 针对多种请求(用户、组等)返回页面。该元素包含第一组元素和可用于进行下一个

NextPageRequest
调用的
GetAsync()
属性。

为了让这些事情变得更容易一些,还可以使用

PageIterator<T>
来获取所需的尽可能多的元素。在此之前,我们有一堆辅助扩展方法,它们缺乏这种通用方法,并且为每种类型分别实现,或多或少像这样:

public static async Task<IEnumerable<DirectoryObject>> GetAll(this Task<IGroupMembersCollectionWithReferencesPage> collectionTask)
{
    // Argument checks, error handling and parameter for max item count omitted for better readability
    var collectionPage = await collectionTask;

    var start = (IEnumerable<DirectoryObject>)(collectionPage);

    while (collectionPage.NextPageRequest != null)
    {
        collectionPage = await collectionPage.NextPageRequest.GetAsync();
        start = start.Concat(collectionPage);
    }

    return start;
}

这些扩展方法允许编写如下内容:

var currentGroupMembers = await serviceClient.Groups[azureGroup.Id].Members.Request().GetAsync().GetAll();

事实上,每种类型都会返回其自己的类型

...Collection...Page
,我们必须编写相同的方法并仅替换给定的类型。

现在

PageIterator<T>
开始发挥作用,我们尝试编写一个适用于所有
Task<ICollectionPage<TItem>>
的通用函数。虽然编写这样的函数非常容易:

public static async Task<IReadOnlyList<TItem>> GetAllWithPageIterator<TItem>(this Task<ICollectionPage<TItem>> pageTask, IBaseClient baseClient)
{
    // Argument checks, error handling and parameter for max item count omitted for better readability
    var page = await pageTask;
    var allElements = new List<TItem>();

    var iterator = PageIterator<TItem>.CreatePageIterator(baseClient, page, item =>
    {
        allElements.Add(item);
        return true;
    });

    await iterator.IterateAsync();
    return allElements;
}

你可以不使用它:

// Extension method explicitly called to better show error message.
var task = serviceClient.Groups[azureGroup.Id].Members.Request().GetAsync();
await IGraphServiceCollectionPageExtensions.GetAllWithPageIterator<DirectoryObject>(task, serviceClient);

Argument 1: cannot convert from 'System.Threading.Tasks.Task<Microsoft.Graph.IGroupMembersCollectionWithReferencesPage>' to 'System.Threading.Tasks.Task<Microsoft.Graph.ICollectionPage<Microsoft.Graph.DirectoryObject>>'

但是

IGroupMembersCollectionWithReferencesPage
实现了
Microsoft.Graph.ICollectionPage<Microsoft.Graph.DirectoryObject>
,但我认为原因是外部类型
Task<T>
避免了用作通用扩展。如果您重写上述扩展方法以仅使用
ICollectionPage<T>
一切都会按预期工作。但这会破坏扩展方法始终用作两行:

var firstPage = await serviceClient.Groups[azureGroup.Id].Members.Request().GetAsync();
var allItems = firstPage.GetAllWithPageIterator(firstPage, serviceClient);

虽然这有效,但对我来说看起来很糟糕(如果你写

(await serviceClient...GetAsync()).GetAll...
)。它看起来并不真正匹配,像
GetAsync().GetAll()
这样具有通用约束的真正流畅的解决方案会很棒。

c# type-inference microsoft-graph-sdks
1个回答
0
投票

虽然问题是基于 v4 Graph SDK 构建的,但我们在 v5 Graph SDK 上构建了解决方案。没有可用于实现通用扩展的共享基类或接口的问题仍然存在。但每种类型的代码始终相同,这对于文本模板来说听起来不错:

<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ assembly name="NetStandard" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ output extension=".cs" #>
//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by a text template.
//     Creation date: <#= DateTime.Now #>
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.Graph.Models;

namespace Microsoft.Graph
{
    public static class GraphServiceCollectionResponseExtensions
    {
<#
    var pageTypes = new List<string>
    {
        "Group",
        "User",
        // Add further needed types here
    };

    foreach (var type in pageTypes)
    {
#>
        public static async Task<IReadOnlyList<<#= type #>>> GetAll(
            this Task<<#= type #>CollectionResponse> collectionTask,
            GraphServiceClient serviceClient,
            int maxCount = int.MaxValue)
        {
            if (collectionTask == null)
                throw new ArgumentNullException(nameof(collectionTask));

            var response = await collectionTask;
            var items = new List<<#= type #>>();

            var iterator = PageIterator<<#= type #>, <#= type #>CollectionResponse>
                .CreatePageIterator(
                    serviceClient,
                    response,
                    item =>
                    {
                        items.Add(item);
                        return items.Count < maxCount;
                    });

            await iterator.IterateAsync();

            return items;
        }

<#
    }
#>
    }
}

通过使用此模板,您可以获得

Group
User
的扩展方法。如果您需要获取其他类型的所有页面,只需将其添加到模板内的
pageTypes
列表中即可。已经测试过的一些类型有
DriveItem
Event
Room
ManagedDevice

生成的代码例如:用户看起来像这样:

public static async Task<IReadOnlyList<User>> GetAll(
    this Task<UserCollectionResponse> collectionTask,
    GraphServiceClient serviceClient,
    int maxCount = int.MaxValue)
{
    if (collectionTask == null)
        throw new ArgumentNullException(nameof(collectionTask));

    var response = await collectionTask;
    var items = new List<User>();

    var iterator = PageIterator<User, UserCollectionResponse>
        .CreatePageIterator(
            serviceClient,
            response,
            item =>
            {
                items.Add(item);
                return items.Count < maxCount;
            });

    await iterator.IterateAsync();

    return items;
}

要在代码中使用它,您可以采用以下示例:

var users = await serviceClient.Users.GetAsync().GetAll();
© www.soinside.com 2019 - 2024. All rights reserved.