本文介绍了ToArrayAsync() 抛出“源 IQueryable 未实现 IAsyncEnumerable"的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在 ASP.NET Core 上有一个 MVC 项目,我的问题与 IQueryable 和异步有关.我在 IQueryable<T> 中编写了以下搜索方法:

I have a MVC project on ASP.NET Core, my problem is connected with IQueryable and asynchronous. I wrote the following method for search in IQueryable<T>:

private IQueryable<InternalOrderInfo> WhereSearchTokens(IQueryable<InternalOrderInfo> query, SearchToken[] searchTokens)
{
    if (searchTokens.Length == 0)
    {
        return query;
    }
    var results = new List<InternalOrderInfo>();
    foreach (var searchToken in searchTokens)
    {
        //search logic, intermediate results are being added to `results` using `AddRange()`
    }

    return results.Count != 0 ? results.Distinct().AsQueryable() : query;
}

我在方法 ExecuteAsync() 中调用它:

I call this in method ExecuteAsync():

public async Task<GetAllInternalOrderInfoResponse> ExecuteAsync(GetAllInternalOrderInfoRequest request)
{
    //rest of the code
    if (searchTokens != null && searchTokens.Any())
    {
        allInternalOrderInfo = WhereSearchTokens(allInternalOrderInfo, searchTokens);
    }
    var orders = await allInternalOrderInfo.Skip(offset).Take(limit).ToArrayAsync();
    //rest of the code
}

当我测试这个时,我在调用 ToArrayAsync()

When I test this I get an InvalidOperationException on line where I call ToArrayAsync()

源 IQueryable 未实现 IAsyncEnumerable.只有实现 IAsyncEnumerable 的源才能用于实体框架异步操作.

我已将 ToArrayAsync() 更改为 ToListAsync() 但没有任何改变.我已经搜索了这个问题一段时间,但解决的问题主要与 DbContext 和实体创建有关.该项目没有安装EntityFramework,由于应用程序架构,最好不要安装.希望有人对我的情况有什么想法.

I had changed ToArrayAsync() to ToListAsync() but nothing have changed. I have searched this problem for a while, but resolved questions are connected mostly with DbContext and entity creating. EntityFramework is not installed for this project and it's better not to do it because of application architecture. Hope someone has any ideas what to do in my situation.

推荐答案

如果你不打算改变你的设计 - 你有几个选择:

If you are not going to change your design - you have several options:

1) 将 AsQueryable 更改为另一个返回 IQueryable 的方法,该方法也实现了 IDbAsyncEnumerable.例如,您可以扩展 EnumerableQuery(由 AsQueryable 返回):

1) Change AsQueryable to another method which returns IQueryable which also implements IDbAsyncEnumerable. For example you can extend EnumerableQuery (which is returned by AsQueryable):

public class AsyncEnumerableQuery<T> : EnumerableQuery<T>, IDbAsyncEnumerable<T> {
    public AsyncEnumerableQuery(IEnumerable<T> enumerable) : base(enumerable) {
    }

    public AsyncEnumerableQuery(Expression expression) : base(expression) {
    }

    public IDbAsyncEnumerator<T> GetAsyncEnumerator() {
        return new InMemoryDbAsyncEnumerator<T>(((IEnumerable<T>) this).GetEnumerator());
    }

    IDbAsyncEnumerator IDbAsyncEnumerable.GetAsyncEnumerator() {
        return GetAsyncEnumerator();
    }

    private class InMemoryDbAsyncEnumerator<T> : IDbAsyncEnumerator<T> {
        private readonly IEnumerator<T> _enumerator;

        public InMemoryDbAsyncEnumerator(IEnumerator<T> enumerator) {
            _enumerator = enumerator;
        }

        public void Dispose() {
        }

        public Task<bool> MoveNextAsync(CancellationToken cancellationToken) {
            return Task.FromResult(_enumerator.MoveNext());
        }

        public T Current => _enumerator.Current;

        object IDbAsyncEnumerator.Current => Current;
    }
}

然后你改变

results.Distinct().AsQueryable()

new AsyncEnumerableQuery<InternalOrderInfo>(results.Distinct())

以后,ToArrayAsync 将不再抛出异常(显然你可以创建自己的扩展方法,如 AsQueryable).

And later, ToArrayAsync will not throw exception any more (obviously you can create your own extension method like AsQueryable).

2) 更改 ToArrayAsync 部分:

public static class EfExtensions {
    public static Task<TSource[]> ToArrayAsyncSafe<TSource>(this IQueryable<TSource> source) {
        if (source == null)
            throw new ArgumentNullException(nameof(source));
        if (!(source is IDbAsyncEnumerable<TSource>))
            return Task.FromResult(source.ToArray());
        return source.ToArrayAsync();
    }
}

并使用 ToArrayAsyncSafe 而不是 ToArrayAsync,如果 IQueryable 不是 IDbAsyncEnumerable,它将回退到同步枚举.在您的情况下,这只发生在查询实际上是内存列表而不是查询时,因此异步执行无论如何都没有意义.

And use ToArrayAsyncSafe instead of ToArrayAsync, which will fallback to synchronous enumeration in case IQueryable is not IDbAsyncEnumerable. In your case this only happens when query is really in-memory list and not query, so async execution does not make sense anyway.

这篇关于ToArrayAsync() 抛出“源 IQueryable 未实现 IAsyncEnumerable"的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

06-30 01:26