2015-10-23 7 views
5

Lets sagen, dass ich eine einfache Repository-Klasse habe, mit einer GetByNames MethodeWie leer IQueryable in einer Asynchron-Repository Methode zurückzukehren

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

Nun, wenn ich es mit Asynchron EntityFramework ToListAsync() Erweiterung verwenden

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

Es wird explodieren, wenn ich leere Liste oder null übergebe, weil Enumerable.Empty<MyObject>().AsQueryable()IDbAsyncEnumerable<MyObject> Schnittstelle nicht implementiert.

Die Quelle IQueryable implementiert IDbAsyncEnumerable nicht. Nur Quellen, die IDbAsyncEnumerable implementieren, können für asynchrone Entity Framework-Vorgänge verwendet werden. Für weitere Details siehe http://go.microsoft.com/fwlink/?LinkId=287068.

Also meine Frage ist, wie kann ich eine leere IQueryable<> zurück, die IDbAsyncEnumerable implementiert, ohne die Datenbank zu schlagen?

+0

ich versucht sein würde, eine Ausnahme zu werfen, wenn der Parameter 'null' und rufen Sie einfach die Abfrage, wenn das Array leer ist. – DavidG

+0

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

Antwort

3

Wenn Sie die DB nicht treffen möchten, müssen Sie höchstwahrscheinlich Ihre eigene Implementierung von IQuerable bereitstellen, die IDbAsyncEnumerable implementiert. Aber ich denke nicht, dass es zu schwer ist. In allen Aufzählern geben Sie einfach null für Current und false für MoveNext zurück. In Dispose einfach nichts tun. Versuch es. Enumerable.Empty<MyObject>().AsQueryable() hat nichts mit Datenbank zu tun, es implementiert definitiv IDbAsyncEnumerable nicht. Sie benötigen eine Implementierung, die nach this funktioniert.

7

Ich endete bei der Implementierung einer Erweiterungs-Methode, die Wrapper, die IDbAsyncEnumerable implementiert zurückgibt. Es basiert auf diesem 10 für spottenden asynchronen Code.

Mit dieser Erweiterung Methode kann ich

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

die großen Werke verwenden.

Umsetzung:

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

Ich verstehe nicht, wie dies ist ein 'async' Anruf wie in Ihrem Code-Beispiel, verwenden Sie es nicht? – Thierry

Verwandte Themen