目前我正在使用 AsyncEnumerable 从 Graph API 获取组成员
public async IAsyncEnumerable<Microsoft.Graph.User> LoadGroupMembers(string GroupId)
{
if (graphClient == null) throw new ArgumentNullException(nameof(graphClient));
var users = await graphClient.Groups[GroupId].Members
.Request()
.GetAsync();
var count = 0;
do
{
foreach (var member in users)
{
if (member is Microsoft.Graph.User user)
{
yield return user;
}
}
}
while (users.NextPageRequest != null && (users = await users.NextPageRequest.GetAsync()).Count > 0);
}
新的 v5 API 摆脱了 NextPageRequest 并用 PageIterator 代替。
var users = await graphClient.Groups[GroupId].Members
.GetAsync();
var pageIterator = PageIterator<User,UserCollectionResponse>
.CreatePageIterator(
graphClient,
users,
// Callback executed for each item in the collection
(u) =>
{
//Don't think I can yield return from here
Console.WriteLine(u.UserName);
return true;
}
);
await pageIterator.IterateAsync();
还有其他方法可以返回记录吗?
我使用以下内容来支持 SDK 版本 5,它借鉴了其他人的答案以及开箱即用的 PageIterator 类型中使用的内部概念(内部也使用了一些反射)。
public static class CollectionPageExtensions
{
public static async IAsyncEnumerable<TEntity> AsAsyncEnumerable<TEntity, TCollectionPage>(this TCollectionPage collectionPage, IBaseClient graphClient, [EnumeratorCancellation] CancellationToken cancellationToken = default)
where TCollectionPage : IBackedModel, IParsable, IAdditionalDataHolder, new()
{
TCollectionPage? currentPage = collectionPage;
do
{
var items = currentPage.BackingStore?.Get<List<TEntity>?>("value");
foreach (TEntity? item in items)
{
cancellationToken.ThrowIfCancellationRequested();
yield return item;
}
var nextPage = await currentPage.GetNextPageAsync<TEntity, TCollectionPage>(graphClient, cancellationToken).ConfigureAwait(false);
if (nextPage == null)
{
yield break;
}
currentPage = nextPage;
}
while (currentPage is not null);
}
private static async Task<TCollectionPage?> GetNextPageAsync<TEntity, TCollectionPage>(this TCollectionPage collectionPage, IBaseClient graphClient, CancellationToken cancellationToken = default)
where TCollectionPage : IBackedModel, IParsable, IAdditionalDataHolder, new()
{
var nextPageLink = ExtractNextLinkFromParsable(collectionPage);
if (string.IsNullOrEmpty(nextPageLink))
{
return default;
}
// Call the MSGraph API to get the next page of results and set that page as the currentPage.
var nextPageRequestInformation = new RequestInformation
{
HttpMethod = Method.GET,
UrlTemplate = nextPageLink
};
// if we have a request configurator, modify the request as desired then execute it to get the next page
return await graphClient.RequestAdapter.SendAsync(nextPageRequestInformation, (parseNode) => new TCollectionPage()).ConfigureAwait(false);
}
private static string ExtractNextLinkFromParsable<TCollectionPage>(TCollectionPage parsableCollection, string nextLinkPropertyName = "OdataNextLink")
where TCollectionPage : IBackedModel, IParsable, IAdditionalDataHolder, new()
{
var nextLinkProperty = parsableCollection.GetType().GetProperty(nextLinkPropertyName);
if (nextLinkProperty != null &&
nextLinkProperty.GetValue(parsableCollection, null) is string nextLinkString
&& !string.IsNullOrEmpty(nextLinkString))
{
return nextLinkString;
}
if (parsableCollection.AdditionalData == null)
return string.Empty;
// the next link property may not be defined in the response schema so we also check its presence in the additional data bag
if (parsableCollection.AdditionalData.TryGetValue(CoreConstants.OdataInstanceAnnotations.NextLink, out var nextLink))
{
if (nextLink != null)
return nextLink.ToString();
}
return string.Empty;
}
}
我还在这张票证中包含了此信息:https://github.com/microsoftgraph/msgraph-sdk-dotnet/issues/733#issuecomment-1884611064