图形 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()
这样具有通用约束的真正流畅的解决方案会很棒。
虽然问题是基于 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();