Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 1 addition & 10 deletions src/Framework/Core/Controls/GridViewDataSetExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,16 +62,7 @@ public static async Task LoadFromQueryableAsync<T>(this IGridViewDataSet<T> data
var filtered = filteringOptions.ApplyToQueryable(queryable);
var sorted = sortingOptions.ApplyToQueryable(filtered);
var paged = pagingOptions.ApplyToQueryable(sorted);
if (paged is not IAsyncEnumerable<T> asyncPaged)
{
throw new ArgumentException($"The specified IQueryable ({queryable.GetType().FullName}), does not support async enumeration. Please use the LoadFromQueryable method.", nameof(queryable));
}

var result = new List<T>();
await foreach (var item in asyncPaged.WithCancellation(cancellationToken))
{
result.Add(item);
}
var result = (await AsyncQueryableImplementation.QueryableToListAsync(paged, cancellationToken)).ToList();
dataSet.Items = result;

if (pagingOptions is IPagingOptionsLoadingPostProcessor pagingOptionsLoadingPostProcessor)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace DotVVM.Framework.Controls
{
public static class AsyncQueryableImplementation
{
public static async Task<IReadOnlyList<T>> QueryableToListAsync<T>(IQueryable<T> queryable, CancellationToken cancellationToken)
{
if (queryable is IAsyncEnumerable<T> asyncPaged)
{
// use IAsyncEnumerable implementation
var result = new List<T>();
await foreach (var item in asyncPaged.WithCancellation(cancellationToken))
{
result.Add(item);
}
return result;
}

var queryableType = queryable.GetType();
if (queryableType is { Namespace: "Marten.Linq", Name: "MartenLinqQueryable`1" })
{
var result = await MartenToListAsync(queryable, queryableType, cancellationToken);
if (result is not null)
{
return result;
}
}

throw new ArgumentException($"The specified IQueryable ({queryable.GetType().FullName}), does not support async enumeration. Please use the LoadFromQueryable method.", nameof(queryable));
}


static MethodInfo? martenMethodCache;
private static Task<IReadOnlyList<T>?> MartenToListAsync<T>(IQueryable<T> queryable, Type queryableType, CancellationToken ct)
{
var toListAsyncMethod = martenMethodCache ?? queryableType.Assembly.GetType("Marten.QueryableExtensions")!.GetMethods().SingleOrDefault(m => m.Name == "ToListAsync" && m.GetParameters() is { Length: 2 } parameters && parameters[1].ParameterType == typeof(CancellationToken));
if (toListAsyncMethod is null)
{
return Task.FromResult<IReadOnlyList<T>?>(null);
}

if (martenMethodCache is null)
Interlocked.CompareExchange(ref martenMethodCache, toListAsyncMethod, null);

var toListMethodGeneric = toListAsyncMethod.MakeGenericMethod(typeof(T));
var result = toListMethodGeneric.Invoke(null, [queryable, ct])!;
return (Task<IReadOnlyList<T>?>)result;
}
}
}
18 changes: 18 additions & 0 deletions src/Framework/Core/Controls/Options/PagingImplementation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ public static async Task<int> QueryableAsyncCount<T>(IQueryable<T> queryable, Ca
// CustomAsyncQueryableCountDelegate, and we do accept PRs adding new heuristics ;) )
return await (
EfCoreAsyncCountHack(queryable, queryableType, ct) ??
MartenAsyncCountHack(queryable, queryableType, ct) ??
StandardAsyncCountHack(queryable, ct)
);
}
Expand All @@ -70,6 +71,23 @@ public static async Task<int> QueryableAsyncCount<T>(IQueryable<T> queryable, Ca
return (Task<int>)countMethodGeneric.Invoke(null, new object[] { queryable, ct })!;
}

static MethodInfo? martenMethodCache;
static Task<int>? MartenAsyncCountHack<T>(IQueryable<T> queryable, Type queryableType, CancellationToken ct)
{
if (!(queryableType.Namespace == "Marten.Linq" && queryableType.Name == "MartenLinqQueryable`1"))
return null;

var countMethod = martenMethodCache ?? queryableType.Assembly.GetType("Marten.QueryableExtensions")!.GetMethods().SingleOrDefault(m => m.Name == "CountAsync" && m.GetParameters() is { Length: 2 } parameters && parameters[1].ParameterType == typeof(CancellationToken));
if (countMethod is null)
return null;

if (martenMethodCache is null)
Interlocked.CompareExchange(ref martenMethodCache, countMethod, null);

var countMethodGeneric = countMethod.MakeGenericMethod(typeof(T));
return (Task<int>)countMethodGeneric.Invoke(null, new object[] { queryable, ct })!;
}

static Task<int> StandardAsyncCountHack<T>(IQueryable<T> queryable, CancellationToken ct)
{
#if NETSTANDARD2_1_OR_GREATER
Expand Down