如何 return 在异步存储库方法中清空 IQueryable
How to return empty IQueryable in an async repository method
假设我有一个简单的存储库 class,有一个 GetByNames
方法
public class MyRepo
{
private readonly MyDbContext _db;
public MyRepo(MyDbContext db)
{
_db = db;
}
public IQueryable<MyObject> GetByNames(IList<string> names)
{
if (names== null || !names.Any())
{
return Enumerable.Empty<MyObject>().AsQueryable();
}
return _db.MyObjects.Where(a => names.Contains(a.Name));
}
}
现在当我将它与异步 EntityFramework ToListAsync()
扩展一起使用时
var myObjects = awawit new MyRepo(_db).GetByNames(names).ToListAsync();
如果我传入空列表或null,它会爆炸,因为Enumerable.Empty<MyObject>().AsQueryable()
没有实现IDbAsyncEnumerable<MyObject>
接口。
The source IQueryable doesn't implement IDbAsyncEnumerable. Only sources that implement IDbAsyncEnumerable can be used for Entity Framework asynchronous operations. For more details see http://go.microsoft.com/fwlink/?LinkId=287068.
所以我的问题是,如何在不访问数据库的情况下 return 实现 IDbAsyncEnumerable
的空 IQueryable<>
?
如果您不想破坏数据库,您很可能必须提供自己的实现 IDbAsyncEnumerable
的空 IQuerable
的实现。但我不认为这太难了。在所有枚举器中,只有 return null
代表 Current
和 false
代表 MoveNext
。在 Dispose
什么都不做。尝试一下。 Enumerable.Empty<MyObject>().AsQueryable()
与数据库无关,肯定没有实现IDbAsyncEnumerable
。根据 this.
,您需要一个实现
我最终实现了一个扩展方法,returns 包装器实现了 IDbAsyncEnumerable
。它基于此 boilerplate implementation 用于模拟异步代码。
有了这个扩展方法我可以使用
return Enumerable.Empty<MyObject>().AsAsyncQueryable();
效果很好。
实施:
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<T> AsAsyncQueryable<T>(this IEnumerable<T> source)
{
return new AsyncQueryableWrapper<T>(source);
}
public static IQueryable<T> AsAsyncQueryable<T>(this IQueryable<T> source)
{
return new AsyncQueryableWrapper<T>(source);
}
}
internal class AsyncQueryableWrapper<T>: IDbAsyncEnumerable<T>, IQueryable<T>
{
private readonly IQueryable<T> _source;
public AsyncQueryableWrapper(IQueryable<T> source)
{
_source = source;
}
public AsyncQueryableWrapper(IEnumerable<T> source)
{
_source = source.AsQueryable();
}
public IDbAsyncEnumerator<T> GetAsyncEnumerator()
{
return new AsyncEnumerator<T>(this.AsEnumerable().GetEnumerator());
}
IDbAsyncEnumerator IDbAsyncEnumerable.GetAsyncEnumerator()
{
return GetAsyncEnumerator();
}
public IEnumerator<T> GetEnumerator()
{
return _source.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public Expression Expression => _source.Expression;
public Type ElementType => _source.ElementType;
public IQueryProvider Provider => new AsyncQueryProvider<T>(_source.Provider);
}
internal class AsyncEnumerable<T> : EnumerableQuery<T>, IDbAsyncEnumerable<T>, IQueryable<T>
{
public AsyncEnumerable(IEnumerable<T> enumerable)
: base(enumerable)
{ }
public AsyncEnumerable(Expression expression)
: base(expression)
{ }
public IDbAsyncEnumerator<T> GetAsyncEnumerator()
{
return new AsyncEnumerator<T>(this.AsEnumerable().GetEnumerator());
}
IDbAsyncEnumerator IDbAsyncEnumerable.GetAsyncEnumerator()
{
return GetAsyncEnumerator();
}
IQueryProvider IQueryable.Provider => new AsyncQueryProvider<T>(this);
}
internal class AsyncQueryProvider<TEntity> : 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<TEntity>(expression);
}
var genericParams = t.GetGenericArguments();
var genericParam = genericParams[0];
var enumerableType = typeof(AsyncEnumerable<>).MakeGenericType(genericParam);
return (IQueryable)Activator.CreateInstance(enumerableType, expression);
}
public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
{
return new AsyncEnumerable<TElement>(expression);
}
public object Execute(Expression expression)
{
return _inner.Execute(expression);
}
public TResult Execute<TResult>(Expression expression)
{
return _inner.Execute<TResult>(expression);
}
public Task<object> ExecuteAsync(Expression expression, CancellationToken cancellationToken)
{
return Task.FromResult(Execute(expression));
}
public Task<TResult> ExecuteAsync<TResult>(Expression expression, CancellationToken cancellationToken)
{
return Task.FromResult(Execute<TResult>(expression));
}
}
internal class AsyncEnumerator<T> : IDbAsyncEnumerator<T>
{
private readonly IEnumerator<T> _inner;
public AsyncEnumerator(IEnumerator<T> inner)
{
_inner = inner;
}
public void Dispose()
{
_inner.Dispose();
}
public Task<bool> MoveNextAsync(CancellationToken cancellationToken)
{
return Task.FromResult(_inner.MoveNext());
}
public T Current => _inner.Current;
object IDbAsyncEnumerator.Current => Current;
}
}
我遇到了同样的问题,并没有弄清楚发生了什么,但我可以用这个解决问题:
public IQueryable<MyObject> GetByNames(IList<string> names)
{
if (names?.Any() != true)
{
return _db.Set<MyObject>().Take(0);
//! or
return _db.MyObjects.Take(0);
}
...
}
假设我有一个简单的存储库 class,有一个 GetByNames
方法
public class MyRepo
{
private readonly MyDbContext _db;
public MyRepo(MyDbContext db)
{
_db = db;
}
public IQueryable<MyObject> GetByNames(IList<string> names)
{
if (names== null || !names.Any())
{
return Enumerable.Empty<MyObject>().AsQueryable();
}
return _db.MyObjects.Where(a => names.Contains(a.Name));
}
}
现在当我将它与异步 EntityFramework ToListAsync()
扩展一起使用时
var myObjects = awawit new MyRepo(_db).GetByNames(names).ToListAsync();
如果我传入空列表或null,它会爆炸,因为Enumerable.Empty<MyObject>().AsQueryable()
没有实现IDbAsyncEnumerable<MyObject>
接口。
The source IQueryable doesn't implement IDbAsyncEnumerable. Only sources that implement IDbAsyncEnumerable can be used for Entity Framework asynchronous operations. For more details see http://go.microsoft.com/fwlink/?LinkId=287068.
所以我的问题是,如何在不访问数据库的情况下 return 实现 IDbAsyncEnumerable
的空 IQueryable<>
?
如果您不想破坏数据库,您很可能必须提供自己的实现 IDbAsyncEnumerable
的空 IQuerable
的实现。但我不认为这太难了。在所有枚举器中,只有 return null
代表 Current
和 false
代表 MoveNext
。在 Dispose
什么都不做。尝试一下。 Enumerable.Empty<MyObject>().AsQueryable()
与数据库无关,肯定没有实现IDbAsyncEnumerable
。根据 this.
我最终实现了一个扩展方法,returns 包装器实现了 IDbAsyncEnumerable
。它基于此 boilerplate implementation 用于模拟异步代码。
有了这个扩展方法我可以使用
return Enumerable.Empty<MyObject>().AsAsyncQueryable();
效果很好。
实施:
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<T> AsAsyncQueryable<T>(this IEnumerable<T> source)
{
return new AsyncQueryableWrapper<T>(source);
}
public static IQueryable<T> AsAsyncQueryable<T>(this IQueryable<T> source)
{
return new AsyncQueryableWrapper<T>(source);
}
}
internal class AsyncQueryableWrapper<T>: IDbAsyncEnumerable<T>, IQueryable<T>
{
private readonly IQueryable<T> _source;
public AsyncQueryableWrapper(IQueryable<T> source)
{
_source = source;
}
public AsyncQueryableWrapper(IEnumerable<T> source)
{
_source = source.AsQueryable();
}
public IDbAsyncEnumerator<T> GetAsyncEnumerator()
{
return new AsyncEnumerator<T>(this.AsEnumerable().GetEnumerator());
}
IDbAsyncEnumerator IDbAsyncEnumerable.GetAsyncEnumerator()
{
return GetAsyncEnumerator();
}
public IEnumerator<T> GetEnumerator()
{
return _source.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public Expression Expression => _source.Expression;
public Type ElementType => _source.ElementType;
public IQueryProvider Provider => new AsyncQueryProvider<T>(_source.Provider);
}
internal class AsyncEnumerable<T> : EnumerableQuery<T>, IDbAsyncEnumerable<T>, IQueryable<T>
{
public AsyncEnumerable(IEnumerable<T> enumerable)
: base(enumerable)
{ }
public AsyncEnumerable(Expression expression)
: base(expression)
{ }
public IDbAsyncEnumerator<T> GetAsyncEnumerator()
{
return new AsyncEnumerator<T>(this.AsEnumerable().GetEnumerator());
}
IDbAsyncEnumerator IDbAsyncEnumerable.GetAsyncEnumerator()
{
return GetAsyncEnumerator();
}
IQueryProvider IQueryable.Provider => new AsyncQueryProvider<T>(this);
}
internal class AsyncQueryProvider<TEntity> : 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<TEntity>(expression);
}
var genericParams = t.GetGenericArguments();
var genericParam = genericParams[0];
var enumerableType = typeof(AsyncEnumerable<>).MakeGenericType(genericParam);
return (IQueryable)Activator.CreateInstance(enumerableType, expression);
}
public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
{
return new AsyncEnumerable<TElement>(expression);
}
public object Execute(Expression expression)
{
return _inner.Execute(expression);
}
public TResult Execute<TResult>(Expression expression)
{
return _inner.Execute<TResult>(expression);
}
public Task<object> ExecuteAsync(Expression expression, CancellationToken cancellationToken)
{
return Task.FromResult(Execute(expression));
}
public Task<TResult> ExecuteAsync<TResult>(Expression expression, CancellationToken cancellationToken)
{
return Task.FromResult(Execute<TResult>(expression));
}
}
internal class AsyncEnumerator<T> : IDbAsyncEnumerator<T>
{
private readonly IEnumerator<T> _inner;
public AsyncEnumerator(IEnumerator<T> inner)
{
_inner = inner;
}
public void Dispose()
{
_inner.Dispose();
}
public Task<bool> MoveNextAsync(CancellationToken cancellationToken)
{
return Task.FromResult(_inner.MoveNext());
}
public T Current => _inner.Current;
object IDbAsyncEnumerator.Current => Current;
}
}
我遇到了同样的问题,并没有弄清楚发生了什么,但我可以用这个解决问题:
public IQueryable<MyObject> GetByNames(IList<string> names)
{
if (names?.Any() != true)
{
return _db.Set<MyObject>().Take(0);
//! or
return _db.MyObjects.Take(0);
}
...
}