Lets say I have a simple repository class, with one GetByNames
method
public class MyRepo
{
private readonly MyDbContext _db;
public My
I ended up implementing an extension method that returns wrapper which implements IDbAsyncEnumerable
. It is based on this boilerplate implementation for mocking async code.
With this extension method I can use
return Enumerable.Empty().AsAsyncQueryable();
which works great.
Implementation:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Linq.Expressions;
using System.Threading;
using System.Threading.Tasks;
namespace MyProject.MyDatabase.Extensions
{
public static class EnumerableExtensions
{
public static IQueryable AsAsyncQueryable(this IEnumerable source)
{
return new AsyncQueryableWrapper(source);
}
public static IQueryable AsAsyncQueryable(this IQueryable source)
{
return new AsyncQueryableWrapper(source);
}
}
internal class AsyncQueryableWrapper: IDbAsyncEnumerable, IQueryable
{
private readonly IQueryable _source;
public AsyncQueryableWrapper(IQueryable source)
{
_source = source;
}
public AsyncQueryableWrapper(IEnumerable source)
{
_source = source.AsQueryable();
}
public IDbAsyncEnumerator GetAsyncEnumerator()
{
return new AsyncEnumerator(this.AsEnumerable().GetEnumerator());
}
IDbAsyncEnumerator IDbAsyncEnumerable.GetAsyncEnumerator()
{
return GetAsyncEnumerator();
}
public IEnumerator GetEnumerator()
{
return _source.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public Expression Expression => _source.Expression;
public Type ElementType => _source.ElementType;
public IQueryProvider Provider => new AsyncQueryProvider(_source.Provider);
}
internal class AsyncEnumerable : EnumerableQuery, IDbAsyncEnumerable, IQueryable
{
public AsyncEnumerable(IEnumerable enumerable)
: base(enumerable)
{ }
public AsyncEnumerable(Expression expression)
: base(expression)
{ }
public IDbAsyncEnumerator GetAsyncEnumerator()
{
return new AsyncEnumerator(this.AsEnumerable().GetEnumerator());
}
IDbAsyncEnumerator IDbAsyncEnumerable.GetAsyncEnumerator()
{
return GetAsyncEnumerator();
}
IQueryProvider IQueryable.Provider => new AsyncQueryProvider(this);
}
internal class AsyncQueryProvider : IDbAsyncQueryProvider
{
private readonly IQueryProvider _inner;
internal AsyncQueryProvider(IQueryProvider inner)
{
_inner = inner;
}
public IQueryable CreateQuery(Expression expression)
{
var t = expression.Type;
if (!t.IsGenericType)
{
return new AsyncEnumerable(expression);
}
var genericParams = t.GetGenericArguments();
var genericParam = genericParams[0];
var enumerableType = typeof(AsyncEnumerable<>).MakeGenericType(genericParam);
return (IQueryable)Activator.CreateInstance(enumerableType, expression);
}
public IQueryable CreateQuery(Expression expression)
{
return new AsyncEnumerable(expression);
}
public object Execute(Expression expression)
{
return _inner.Execute(expression);
}
public TResult Execute(Expression expression)
{
return _inner.Execute(expression);
}
public Task