2015-10-23 25 views
5

可以說我有一個簡單的倉儲類,一個GetByNames方法現在如何空車返回的IQueryable在異步庫方法

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(); 

如果我通過空列表或空因爲它會炸掉,因爲Enumerable.Empty<MyObject>().AsQueryable()沒有實現IDbAsyncEnumerable<MyObject>接口。

源IQueryable不實現IDbAsyncEnumerable。只有實現IDbAsyncEnumerable的源才能用於實體框架異步操作。欲瞭解更多詳情,請參閱http://go.microsoft.com/fwlink/?LinkId=287068

所以我的問題是,我怎麼能返回一個空IQueryable<>實現IDbAsyncEnumerable,而無需訪問數據庫?

+0

我會忍不住拋出一個異常,如果該參數是'null'並調用查詢,如果數組爲空。 – DavidG

+0

http://stackoverflow.com/a/26330298/870604 – ken2k

回答

3

如果你不想擊中數據庫,你很可能必須提供自己的實現IQuerable,實現IDbAsyncEnumerable。但我不認爲這太難。在所有的普查員只需返回nullCurrentfalseMoveNext。在Dispose什麼都不做。嘗試一下。 Enumerable.Empty<MyObject>().AsQueryable()與數據庫無關,它絕對不會執行IDbAsyncEnumerable。根據this,您需要執行此操作。

7

我最終實現了一個擴展方法,該方法返回實現了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; 
    } 
} 
+0

我不明白這是一個'async'調用,就像你的代碼行中的例子,你不使用它? – Thierry