【问题标题】:How to return empty IQueryable in an async repository method如何在异步存储库方法中返回空 IQueryable
【发布时间】:2016-01-23 04:13:52
【问题描述】:

假设我有一个简单的存储库类,有一个 GetByNames 方法

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

如果我传入空列表或 null,它会爆炸,因为 Enumerable.Empty&lt;MyObject&gt;().AsQueryable() 没有实现 IDbAsyncEnumerable&lt;MyObject&gt; 接口。

源 IQueryable 未实现 IDbAsyncEnumerable。只有实现 IDbAsyncEnumerable 的源才能用于实体框架异步操作。更多详情请见http://go.microsoft.com/fwlink/?LinkId=287068

所以我的问题是,我怎样才能返回一个实现IDbAsyncEnumerable 的空IQueryable&lt;&gt;,而不访问数据库?

【问题讨论】:

标签: entity-framework async-await iqueryable


【解决方案1】:

我最终实现了一个扩展方法,该方法返回实现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;
    }
}

【讨论】:

  • 我无法理解这是一个async 调用,就像您的代码示例中的那样,您不使用它吗?
【解决方案2】:

如果您不想访问数据库,您很可能必须提供自己的空IQuerable 实现,它实现了IDbAsyncEnumerable。但我不认为这太难了。在所有枚举器中,只为Current 返回null,为MoveNext 返回false。在Dispose 中什么都不做。试试吧。 Enumerable.Empty&lt;MyObject&gt;().AsQueryable() 与数据库无关,它绝对没有实现IDbAsyncEnumerable。根据this,您需要一个实现。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-01-03
    • 1970-01-01
    • 1970-01-01
    • 2013-07-24
    • 2019-09-05
    相关资源
    最近更新 更多