CommunityToolkit / CommunityToolkit/Windows
Implement IncrementalLoadingCollection from IAsyncEnumerable
- Vorherrschende Sprache
- C#
- Sterne
- 1.1k
- Forks
- 166
- PR-Merge-Kennzahlen
- Keine gemergten PRs in 30 T.
Beschreibung
### Overview
To implement an IncrementalLoadingCollection, you need:
1. [IIncrementalSource](https://github.com/CommunityToolkit/dotnet/blob/main/src/CommunityToolkit.Common/IncrementalLoadingCollection/IIncrementalSource.cs) from the dotnet repo, `CommunityToolkit.Common` namespace
2. [IncrementalLoadingCollection](https://github.com/CommunityToolkit/WindowsCommunityToolkit/blob/main/Microsoft.Toolkit.Uwp/IncrementalLoadingCollection/IncrementalLoadingCollection.cs) from the WindowsCommunityToolkit repo, `Microsoft.Toolkit.Uwp` namespace.
The API design is quite dated and requires creating a source class implementing `IIncrementalSource`, and creating `IncrementalLoadingCollection`.
IIncrementalSource requires to implement a method returning `Task>` that is modernly expressed with `IAsyncEnumerable`.
`IAsyncEnumerable` allows cycling over the collection in a fully asynchronous way and it's the perfect fit for a modern IncrementalLoadingCollection. I called this new type `AsyncLoadingCollection`
### API breakdown
```csharp
public class AsyncLoadingCollection(IAsyncEnumerable source, uint itemsPerPage = 25) : ObservableCollection, ISupportIncrementalLoading
{
private IAsyncEnumerator? _asyncEnumerator = source.GetAsyncEnumerator();
private readonly SemaphoreSlim _mutex = new(1, 1);
public bool HasMoreItems => _asyncEnumerator != null;
public IAsyncOperation LoadMoreItemsAsync(uint count = 0) =>
LoadMoreItemsAsync(count == 0 ? itemsPerPage : count, default)
.AsAsyncOperation();
private async Task LoadMoreItemsAsync(uint count, CancellationToken cancellationToken)
{
await _mutex.WaitAsync(cancellationToken);
if (cancellationToken.IsCancellationRequested || !HasMoreItems)
return new LoadMoreItemsResult(0);
uint itemsLoaded = 0;
var itemsToLoad = Math.Min(itemsPerPage, count);
try
{
while (itemsLoaded < itemsToLoad)
{
if (await _asyncEnumerator!.MoveNextAsync(cancellationToken).ConfigureAwait(false))
{
Add(_asyncEnumerator!.Current);
itemsLoaded++;
}
else
{
// Dispose the enumerator when we're done
await _asyncEnumerator!.DisposeAsync();
_asyncEnumerator = null;
break;
}
}
}
catch (OperationCanceledException)
{
// The operation has been canceled using the Cancellation Token.
await _asyncEnumerator!.DisposeAsync();
_asyncEnumerator = null;
}
catch (Exception)
{
await _asyncEnumerator!.DisposeAsync();
_asyncEnumerator = null;
throw;
}
finally
{
_mutex.Release();
}
return new LoadMoreItemsResult(itemsLoaded);
}
}
```
### Usage example
```csharp
public static async IAsyncEnumerable GetSampleData()
{
for (var i = 0; i < 100; i++)
{
await Task.Delay(i * 10);
yield return $"string {i}";
}
}
var myAsyncCollection = new AsyncLoadingCollection(GetSampleData());
// this is needed for the initial load (viewControls like DataGrid don't call it automatically after DataContext change)
await myAsyncCollection.LoadMoreItemsAsync();
```
Then you can bind myAsyncCollection to a DataGrid as ItemsSource
### Breaking change?
No, it's an addition to IncrementalLoadingCollection.
### Alternatives
After few days of research, I couldn't find any other implementation of an IncrementalLoadingCollection using IAsyncEnumerable.
Beitragsleitfaden
Rechercherichtung
Beginnen Sie mit dem Lesen von src/CommunityToolkit.Common/IncrementalLoadingCollection/IIncrementalSource.cs und der verknüpften WindowsCommunityToolkit IncrementalLoadingCollection.cs. Vergleichen Sie diese APIs mit dem vorgeschlagenen Einstiegspunkt AsyncLoadingCollection und seinem Beispiel für die Verwendung von IAsyncEnumerable. Die Arbeit ist abgeschlossen, wenn eine additive Collection inkrementelles Laden aus einem asynchronen Enumerator unterstützt und als ItemsSource gebunden werden kann, ohne IncrementalLoadingCollection zu ändern.
Vom Indexierungsmodell aus dem Issue-Text verfasst.
Bewertung
- Tech-Stack
- csharp
- Bereich
- frontend
- Issue-Typ
- Feature
- Schwierigkeit
- 4/5
- Geschätzter Aufwand
- 3-5 Tage
- Aktivitätsstatus
- Veraltet
- Klarheit
- Größtenteils klar
- Anfängerfreundlichkeit
- 35/100