2015-10-23 11 views
5

powiedzmy, że mam proste klasy repozytorium, z jednym GetByNames metodyJak zwrócić pusty IQueryable w metodzie repozytorium asynchroniczny

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

Teraz, kiedy go używać z asynchronicznym EntityFramework ToListAsync() przedłużacza

var myObjects = awawit new MyRepo(_db).GetByNames(names).ToListAsync(); 

Wystąpi, jeśli przejdę na pustą listę lub wartość pustą, ponieważ Enumerable.Empty<MyObject>().AsQueryable() nie implementuje interfejsu IDbAsyncEnumerable<MyObject>.

Źródło IQueryable nie implementuje IDbAsyncEnumerable. Tylko źródła implementujące IDbAsyncEnumerable mogą być używane do asynchronicznych operacji Entity Framework. Aby uzyskać więcej informacji, patrz http://go.microsoft.com/fwlink/?LinkId=287068.

Więc moje pytanie brzmi, jak mogę wrócić pusty IQueryable<> który implementuje IDbAsyncEnumerable, bez uderzania bazy danych?

+0

byłbym skłonny do wyjątek, jeśli parametr jest 'null' i zadzwoń zapytania jeśli tablica jest pusta. – DavidG

+0

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

Odpowiedz

3

Jeśli nie chcesz uderzać w DB, najprawdopodobniej musisz podać własną implementację pustego IQuerable, która implementuje IDbAsyncEnumerable. Ale nie sądzę, że to jest zbyt trudne. We wszystkich modułach wyliczających wystarczy zwrócić null dla Current i false dla MoveNext. W Dispose po prostu nic nie rób. Spróbuj. Enumerable.Empty<MyObject>().AsQueryable() nie ma nic wspólnego z bazą danych, zdecydowanie nie implementuje IDbAsyncEnumerable. Potrzebujesz implementacji, która działa zgodnie z this.

7

Skończyłem na wdrażaniu metody rozszerzającej, która zwraca opakowanie, które implementuje IDbAsyncEnumerable. Opiera się on na tym boilerplate implementation dla wyśmiewania kodu asynchronicznego.

Dzięki tej metodzie wydłużania mogę używać

return Enumerable.Empty<MyObject>().AsAsyncQueryable(); 

który działa świetnie.

Realizacja:

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

Nie mogę zrozumieć, jak to jest połączenie asynchroniczne, jak w przykładzie linii kodu, nie używasz go? – Thierry

Powiązane problemy