Itselves.Tasks.Linq
1.0.2
dotnet add package Itselves.Tasks.Linq --version 1.0.2
NuGet\Install-Package Itselves.Tasks.Linq -Version 1.0.2
<PackageReference Include="Itselves.Tasks.Linq" Version="1.0.2" />
<PackageVersion Include="Itselves.Tasks.Linq" Version="1.0.2" />
<PackageReference Include="Itselves.Tasks.Linq" />
paket add Itselves.Tasks.Linq --version 1.0.2
#r "nuget: Itselves.Tasks.Linq, 1.0.2"
#:package Itselves.Tasks.Linq@1.0.2
#addin nuget:?package=Itselves.Tasks.Linq&version=1.0.2
#tool nuget:?package=Itselves.Tasks.Linq&version=1.0.2
System.Tasks.Linq
LINQ extension methods for Task<IEnumerable<T>>. Chain async data-returning operations without intermediate await expressions — the full LINQ surface, lifted into task space.
Installation
dotnet add package Itselves.Tasks.Linq
Compatibility
netstandard2.0 — works on .NET Framework 4.6.1+, .NET Core 2.0+, .NET 5+, Xamarin, Unity.
Usage
Add the using and call LINQ methods directly on any Task<IEnumerable<T>>:
using System.Tasks.Linq.Extensions;
// Without System.Tasks.Linq
var result = (await GetOrdersAsync()).Where(o => o.IsActive).Select(o => o.Total).Sum();
// With System.Tasks.Linq
var result = await GetOrdersAsync().Where(o => o.IsActive).Select(o => o.Total).Sum();
The await moves to the end of the chain. Each operator awaits the task internally, applies the standard LINQ operation, and returns a new Task for the next operator to consume.
Working with other collection types
Methods returning Task<List<T>>, Task<T[]>, or any other collection type expose AsEnumerable() to widen to Task<IEnumerable<T>>, after which all LINQ operators are available:
// Task<List<T>> from EF Core ToListAsync(), Task<T[]>, Task<IReadOnlyList<T>>, etc.
var result = await GetOrdersAsync() // Task<List<Order>>
.AsEnumerable() // Task<IEnumerable<Order>>
.Where(o => o.IsActive)
.Select(o => o.Total)
.Sum();
AsEnumerable() is provided for:
| Type | Common source |
|---|---|
Task<IReadOnlyCollection<T>> |
Read-only APIs, domain models |
Task<IReadOnlyList<T>> |
Read-only indexed APIs |
Task<ICollection<T>> |
Mutable collection interfaces |
Task<IList<T>> |
Mutable indexed interfaces |
Task<List<T>> |
EF Core ToListAsync(), most ORMs |
Task<T[]> |
ToArrayAsync(), fixed-size results |
API reference
All standard System.Linq.Enumerable operators are available on Task<IEnumerable<T>>.
Aggregation
Task<TSource> Aggregate(func)
Task<TAccumulate> Aggregate(seed, func)
Task<TResult> Aggregate(seed, func, resultSelector)
Task<double> Average() // int, long, float, double, decimal + nullable variants
Task<double> Average(selector)
Task<int> Count()
Task<int> Count(predicate)
Task<long> LongCount()
Task<long> LongCount(predicate)
Task<T> Max() // int, long, float, double, decimal + nullable + generic
Task<T> Max(selector)
Task<T> Min() // same overloads as Max
Task<T> Min(selector)
Task<T> Sum() // int, long, float, double, decimal + nullable variants
Task<T> Sum(selector)
Filtering
Task<bool> All(predicate)
Task<bool> Any()
Task<bool> Any(predicate)
Task<bool> Contains(value, comparer?)
Task<bool> SequenceEqual(second, comparer?)
Task<IEnumerable<T>> Where(predicate)
Task<IEnumerable<T>> Where((element, index) => predicate)
Projection
Task<IEnumerable<TResult>> Select(selector)
Task<IEnumerable<TResult>> Select((element, index) => selector)
Task<IEnumerable<TResult>> SelectMany(selector)
Task<IEnumerable<TResult>> SelectMany((element, index) => selector)
Task<IEnumerable<TResult>> SelectMany(collectionSelector, resultSelector)
Task<IEnumerable<TResult>> SelectMany((element, index) => collectionSelector, resultSelector)
Element access
Task<T> ElementAt(index)
Task<T?> ElementAtOrDefault(index)
Task<T> First()
Task<T> First(predicate)
Task<T?> FirstOrDefault()
Task<T?> FirstOrDefault(predicate)
Task<T> Last()
Task<T> Last(predicate)
Task<T?> LastOrDefault()
Task<T?> LastOrDefault(predicate)
Task<T> Single()
Task<T> Single(predicate)
Task<T?> SingleOrDefault()
Task<T?> SingleOrDefault(predicate)
Ordering
Task<IOrderedEnumerable<T>> OrderBy(keySelector, comparer?)
Task<IOrderedEnumerable<T>> OrderByDescending(keySelector, comparer?)
Task<IOrderedEnumerable<T>> ThenBy(keySelector, comparer?)
Task<IOrderedEnumerable<T>> ThenByDescending(keySelector, comparer?)
Task<IEnumerable<T>> Reverse()
ThenBy and ThenByDescending extend Task<IOrderedEnumerable<T>>, so multi-key sorting chains stay fully async without a mid-chain await.
Partitioning
Task<IEnumerable<T>> Append(element)
Task<IEnumerable<T>> Prepend(element)
Task<IEnumerable<T>> DefaultIfEmpty()
Task<IEnumerable<T>> DefaultIfEmpty(defaultValue)
Task<IEnumerable<T>> Skip(count)
Task<IEnumerable<T>> SkipWhile(predicate)
Task<IEnumerable<T>> SkipWhile((element, index) => predicate)
Task<IEnumerable<T>> Take(count)
Task<IEnumerable<T>> TakeWhile(predicate)
Task<IEnumerable<T>> TakeWhile((element, index) => predicate)
Grouping and joining
Task<IEnumerable<IGrouping<TKey, T>>> GroupBy(keySelector, comparer?)
Task<IEnumerable<IGrouping<TKey, E>>> GroupBy(keySelector, elementSelector, comparer?)
Task<IEnumerable<TResult>> GroupBy(keySelector, resultSelector, comparer?)
Task<IEnumerable<TResult>> GroupBy(keySelector, elementSelector, resultSelector, comparer?)
Task<IEnumerable<TResult>> GroupJoin(inner, outerKey, innerKey, resultSelector, comparer?)
Task<IEnumerable<TResult>> Join(inner, outerKey, innerKey, resultSelector, comparer?)
Set operations
Task<IEnumerable<T>> Concat(second)
Task<IEnumerable<T>> Distinct(comparer?)
Task<IEnumerable<T>> Except(second, comparer?)
Task<IEnumerable<T>> Intersect(second, comparer?)
Task<IEnumerable<T>> Union(second, comparer?)
Conversion
Task<IEnumerable<T>> AsEnumerable()
Task<IEnumerable<TResult>> Cast<TResult>() // Task<IEnumerable> (non-generic source)
Task<IEnumerable<TResult>> OfType<TResult>() // Task<IEnumerable> (non-generic source)
Task<T[]> ToArray()
Task<List<T>> ToList()
Task<Dictionary<TKey, T>> ToDictionary(keySelector, comparer?)
Task<Dictionary<TKey, TElement>> ToDictionary(keySelector, elementSelector, comparer?)
Task<ILookup<TKey, T>> ToLookup(keySelector, comparer?)
Task<ILookup<TKey, TElement>> ToLookup(keySelector, elementSelector, comparer?)
Cast and OfType are only available on Task<IEnumerable> (the non-generic interface) because they are type-changing operations that require an untyped source.
Zip
Task<IEnumerable<TResult>> Zip(second, resultSelector)
Examples
Pipeline with multiple operators
var topSpenders = await dbContext.GetCustomersAsync()
.Where(c => c.IsActive)
.SelectMany(c => c.Orders)
.GroupBy(o => o.CustomerId, o => o.Total, (id, totals) => (id, total: totals.Sum()))
.OrderByDescending(x => x.total)
.Take(10)
.ToList();
Starting from a Task<List<T>>
double average = await repository.GetTransactionsAsync() // Task<List<Transaction>>
.AsEnumerable()
.Where(t => t.Date >= DateTime.Today.AddDays(-30))
.Average(t => t.Amount);
Chaining OrderBy with ThenBy
var sorted = await GetProductsAsync()
.OrderBy(p => p.Category)
.ThenBy(p => p.Name)
.ToList();
Behaviour notes
- Every operator awaits the upstream
Task<IEnumerable<T>>exactly once and delegates directly to the correspondingSystem.Linq.Enumerablemethod. There is no buffering or side-effect beyond what standard LINQ does. - Other collection types (
List<T>,T[],IReadOnlyList<T>, etc.) expose onlyAsEnumerable(), which upcasts toTask<IEnumerable<T>>. All LINQ logic lives in one place — there is no duplication across collection types. - Exceptions from the underlying task propagate normally when awaited.
- Deferred LINQ operators (
Where,Select,SelectMany, etc.) remain deferred — the selector/predicate runs when the result is enumerated, not when the task is awaited. - Operators that throw on invalid input (
First,Single,ElementAt,Aggregatewithout seed on empty sequence, etc.) preserve the same exception semantics as theirEnumerablecounterparts.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.