using System.Linq.Expressions; using System.Collections; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Query; #nullable disable namespace GiecChallenge.Tests { public static class ICollectionExtensions { public static IQueryable AsAsyncQueryable(this ICollection source) => new AsyncQueryable(source.AsQueryable()); } internal class AsyncQueryable : IAsyncEnumerable, IQueryable { private IQueryable Source; public AsyncQueryable(IQueryable source) { Source = source; } public Type ElementType => typeof(T); public Expression Expression => Source.Expression; public IQueryProvider Provider => new AsyncQueryProvider(Source.Provider); public IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) { return new AsyncEnumeratorWrapper(Source.GetEnumerator()); } public IEnumerator GetEnumerator() => Source.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } internal class AsyncQueryProvider : IQueryProvider, IAsyncQueryProvider { private readonly IQueryProvider Source; public AsyncQueryProvider(IQueryProvider source) { Source = source; } public IQueryable CreateQuery(Expression expression) => Source.CreateQuery(expression); public IQueryable CreateQuery(Expression expression) => new AsyncQueryable(Source.CreateQuery(expression)); public object Execute(Expression expression) => Execute(expression); public TResult Execute(Expression expression) => Source.Execute(expression); public TResult ExecuteAsync(Expression expression, CancellationToken cancellationToken) { var expectedResultType = typeof(TResult).GetGenericArguments()[0]; var executionResult = typeof(IQueryProvider) .GetMethod( name: nameof(IQueryProvider.Execute), genericParameterCount: 1, types: new[] {typeof(Expression)}) .MakeGenericMethod(expectedResultType) .Invoke(this, new[] {expression}); return (TResult) typeof(Task).GetMethod(nameof(Task.FromResult)) ?.MakeGenericMethod(expectedResultType) .Invoke(null, new[] {executionResult}); } TResult IAsyncQueryProvider.ExecuteAsync(Expression expression, CancellationToken cancellationToken) => ExecuteAsync(expression, cancellationToken); } internal class AsyncEnumeratorWrapper : IAsyncEnumerator { private readonly IEnumerator Source; public AsyncEnumeratorWrapper(IEnumerator source) { Source = source; } public T Current => Source.Current; public ValueTask DisposeAsync() { return new ValueTask(Task.CompletedTask); } public ValueTask MoveNextAsync() { return new ValueTask(Source.MoveNext()); } } }