如何將可枚舉變為AsyncEnumerable?

.net c# entity-framework-core linq

我有一些代碼使用Entity Framework Core中提供的異步擴展方法:

public async Task<MyResult> DoQuery<T>(IQueryable<T> queryable)
{
    var count = await queryable.CountAsync();
    var firstItems = await queryable
        .Take(5)
        .ToArrayAsync();

    return new MyResult(count, firstItems);
}

當我賦予函數的IQueryable直接來自EF時,這很有用。我還想重用這段代碼來對LINQ-to-objects“查詢”做一些邏輯:

var evens = Enumerable.Range(0, 10).Where(i => i % 2 == 0);
var result = DoQuery(evens.AsQueryable());

這失敗了(這並不太令人驚訝):

System.InvalidOperationException:源IQueryable的提供程序未實現IAsyncQueryProvider。只有實現IEntityQueryProvider的提供程序才能用於Entity Framework異步操作。

它看起來像重構有序,但我很好奇:有沒有辦法將普通的可枚舉變成同步處理CountAsync的“虛擬” AsyncEnumerableAsyncQueryable

一般承認的答案

您需要創建內存中的DbAsyncQueryProvider來處理異步查詢。有關於如何做的詳細解釋在這裡 。滾動到有關使用異步查詢進行測試的部分。以下是該鏈接的代碼複製和粘貼:

using System.Collections.Generic; 
using System.Data.Entity.Infrastructure; 
using System.Linq; 
using System.Linq.Expressions; 
using System.Threading; 
using System.Threading.Tasks; 

namespace TestingDemo 
{ 
    internal class TestDbAsyncQueryProvider<TEntity> : IDbAsyncQueryProvider 
    { 
        private readonly IQueryProvider _inner; 

        internal TestDbAsyncQueryProvider(IQueryProvider inner) 
        { 
            _inner = inner; 
        } 

        public IQueryable CreateQuery(Expression expression) 
        { 
            return new TestDbAsyncEnumerable<TEntity>(expression); 
        } 

        public IQueryable<TElement> CreateQuery<TElement>(Expression expression) 
        { 
            return new TestDbAsyncEnumerable<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 TestDbAsyncEnumerable<T> : EnumerableQuery<T>, IDbAsyncEnumerable<T>, IQueryable<T> 
    { 
        public TestDbAsyncEnumerable(IEnumerable<T> enumerable) 
            : base(enumerable) 
        { } 

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

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

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

        IQueryProvider IQueryable.Provider 
        { 
            get { return new TestDbAsyncQueryProvider<T>(this); } 
        } 
    } 

    internal class TestDbAsyncEnumerator<T> : IDbAsyncEnumerator<T> 
    { 
        private readonly IEnumerator<T> _inner; 

        public TestDbAsyncEnumerator(IEnumerator<T> inner) 
        { 
            _inner = inner; 
        } 

        public void Dispose() 
        { 
            _inner.Dispose(); 
        } 

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

        public T Current 
        { 
            get { return _inner.Current; } 
        } 

        object IDbAsyncEnumerator.Current 
        { 
            get { return Current; } 
        } 
    } 
}


Related

許可下: CC-BY-SA with attribution
不隸屬於 Stack Overflow
許可下: CC-BY-SA with attribution
不隸屬於 Stack Overflow