CommunityToolkit / CommunityToolkit/Windows

Implement IncrementalLoadingCollection from IAsyncEnumerable

Aperta
#167 4 commenti 0 reazioni 0 assegnatari Vedi su GitHub
components::collections enhancement feature request :mailbox_with_mail:
Lingua principale
C#
Stelle
1.1k
Fork
166
Metriche di merge delle PR
Nessuna PR unita negli ultimi 30g

Descrizione

### 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.

Guida per i contributori

Apri la guida per i contributori

Direzione di ricerca

Iniziare leggendo src/CommunityToolkit.Common/IncrementalLoadingCollection/IIncrementalSource.cs e il file IncrementalLoadingCollection.cs di WindowsCommunityToolkit collegato. Confrontare queste API con il punto di ingresso proposto AsyncLoadingCollection e il relativo esempio di utilizzo di IAsyncEnumerable. Il lavoro è completato quando una collection aggiuntiva supporta il caricamento incrementale da un enumerable asincrono e può essere associata come ItemsSource senza modificare IncrementalLoadingCollection.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Valutazione

Stack tecnologico
csharp
Ambito
frontend
Tipo di issue
Funzionalità
Difficoltà
4/5
Tempo stimato
3-5 giorni
Stato di attività
Ferma
Chiarezza
Abbastanza chiara
Idoneità per principianti
35/100

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.