如何将C# Cust类转换为支持异步查询的IQueryable对象?
解决IQueryable不支持IAsyncQueryProvider的问题
报错的核心原因是:通过List<T>.AsQueryable()得到的是Linq to Objects的查询提供者,它没有实现EF异步操作所需的IAsyncQueryProvider接口,因此无法直接用于CountAsync这类EF异步方法。以下是几种可行的解决办法:
方法一:使用EF Core内存数据库(推荐用于测试/模拟场景)
如果需要模拟EF的查询环境,用EF Core的InMemory数据库可以直接获取支持异步的IQueryable<Cust>:
- 定义包含
Cust实体的DbContext:
public class AppDbContext : DbContext { public DbSet<Cust> Custs { get; set; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseInMemoryDatabase(databaseName: "TestDb"); } }
- 初始化数据并获取支持异步的IQueryable:
using var context = new AppDbContext(); // 添加测试数据 context.Custs.AddRange(new List<Cust> { new Cust(), new Cust() }); context.SaveChanges(); // 此处的context.Custs是支持IAsyncQueryProvider的IQueryable<Cust> var queryable = context.Custs.AsQueryable(); // 可直接传入CountCust方法 await CountCust(queryable, CancellationToken.None);
方法二:自定义异步查询提供者包装类
如果不想依赖EF的InMemory数据库,可以自行实现IAsyncQueryProvider,将普通IQueryable包装为支持异步的版本。以下是简化实现:
public class AsyncQueryProvider<T> : IAsyncQueryProvider { private readonly IQueryProvider _innerProvider; public AsyncQueryProvider(IQueryProvider innerProvider) { _innerProvider = innerProvider; } public IQueryable CreateQuery(Expression expression) { return new AsyncEnumerable<T>(expression, this); } public IQueryable<TElement> CreateQuery<TElement>(Expression expression) { return new AsyncEnumerable<TElement>(expression, this); } public object Execute(Expression expression) { return _innerProvider.Execute(expression); } public TResult Execute<TResult>(Expression expression) { return _innerProvider.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)); } } public class AsyncEnumerable<T> : EnumerableQuery<T>, IAsyncEnumerable<T>, IQueryable<T> { public AsyncEnumerable(Expression expression) : base(expression) { } public AsyncEnumerable(IEnumerable<T> enumerable) : base(enumerable) { } public AsyncEnumerable(Expression expression, IAsyncQueryProvider provider) : base(expression) { Provider = provider; } public new IAsyncQueryProvider Provider { get; } public IAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = default) { return new AsyncEnumerator<T>(this.AsEnumerable().GetEnumerator()); } } public class AsyncEnumerator<T> : IAsyncEnumerator<T> { private readonly IEnumerator<T> _innerEnumerator; public AsyncEnumerator(IEnumerator<T> innerEnumerator) { _innerEnumerator = innerEnumerator; } public T Current => _innerEnumerator.Current; public ValueTask DisposeAsync() { _innerEnumerator.Dispose(); return ValueTask.CompletedTask; } public ValueTask<bool> MoveNextAsync() { return ValueTask.FromResult(_innerEnumerator.MoveNext()); } }
使用时直接包装普通IQueryable:
var list = new List<Cust> { new Cust(), new Cust() }; var asyncQueryable = new AsyncEnumerable<Cust>(list.AsQueryable().Expression, new AsyncQueryProvider<Cust>(list.AsQueryable().Provider)); await CountCust(asyncQueryable, CancellationToken.None);
方法三:在CountCust方法中兼容普通IQueryable(妥协方案)
如果只是希望方法兼容两种IQueryable,可以在CountCust内部做判断,遇到不支持异步的提供者时,改用同步计数再包装为Task:
public async Task<int> CountCust(IQueryable<Cust> query, CancellationToken cancellationToken) { if (query.Provider is IAsyncQueryProvider) { return await query.CountAsync(cancellationToken); } else { // 同步计数后返回已完成的Task return query.Count(); } }
内容的提问来源于stack exchange,提问作者EBDS
相关产品推荐
相关产品推荐

