CommunityToolkit / CommunityToolkit/Windows

Implement IncrementalLoadingCollection from IAsyncEnumerable

Offen
#167 4 Kommentare 0 Reaktionen 0 zugewiesene Personen Auf GitHub ansehen
components::collections enhancement feature request :mailbox_with_mail:
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

Beitragsleitfaden öffnen

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

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.