“ AsyncEnumerableReader”已达到配置的最大大小,但未使用AsyncEnumerable - c#

我正在使用EF Core 3.1.1(dotnet core 3.1.1)。我想退回大量的Car实体。不幸的是,我收到以下错误消息:

'AsyncEnumerableReader' reached the configured maximum size of the buffer when enumerating a value of type 'Microsoft.EntityFrameworkCore.Internal.InternalDbSet`...

我知道关于相同错误还有另一个回答的问题。但是我没有做明确的异步操作。

[HttpGet]
[ProducesResponseType(200, Type = typeof(Car[]))]
public IActionResult Index()
{
   return Ok(_carsDataModelContext.Cars.AsEnumerable());
}

_carDataModelContext.Car只是一个简单的实体,将一对一映射到数据库中的表。 public virtual DbSet<Car> Cars { get; set; }

最初,我返回Ok(_carsDataModelContext.Cars.AsQueryable())是因为我们需要支持OData。但是要确保不是OData搞砸了,我试图返回AsEnumerable,然后从方法中删除“ [EnableQuery]”属性。但这仍然以相同的错误结尾。

解决此问题的唯一方法是,如果我返回Ok(_carsDataModelContext.Cars.ToList())

参考方案

所有Ef Core IQueryable<T>实现(DbSet<T>EntityQueryable<T>)也实现标准的IAsyncEnumerable<T>接口(从.NET Core 3使用时),因此AsEnumerable()AsQueryable()AsAsyncEnumerable()只需返回相同的接口实例强制转换为相应的接口。

您可以使用以下代码段轻松地进行验证:

var queryable = _carsDataModelContext.Cars.AsQueryable();
var enumerable = queryable.AsEnumerable();
var asyncEnumerable = queryable.AsAsyncEnumerable();
Debug.Assert(queryable == enumerable && queryable == asyncEnumerable);

因此,即使您没有显式返回IAsyncEnumerable<T>,底层对象也会实现它,并且可以对其进行查询。知道Asp.Net Core是自然的异步框架,我们可以放心地假设它检查对象是否实现了新的标准IAsyncEnumerable<T>,并在幕后使用它代替了IEnumerable<T>

当然,当您使用ToList()时,返回的List<T>类不会实现IAsyncEnumerable<T>,因此唯一的选择是使用IEnumerable<T>

这应该解释3.1行为。请注意,在3.0之前,没有标准的IAsyncEnumerable<T>接口。 EF Core正在实现并返回其自己的异步接口,但是.Net Core基础结构并未意识到这一点,因此无法代表您使用它。

不使用ToList() / ToArray()和类似方法而强制执行先前行为的唯一方法是隐藏基础源(因此IAsyncEnumerable<T>)。

对于IEnumerable<T>,这很容易。您需要做的就是创建使用C#迭代器的自定义扩展方法,例如:

public static partial class Extensions
{
    public static IEnumerable<T> ToEnumerable<T>(this IEnumerable<T> source)
    {
        foreach (var item in source)
            yield return item;
    }
}

然后使用

return Ok(_carsDataModelContext.Cars.ToEnumerable());

如果要返回IQueryable<T>,事情会变得更加艰难。创建自定义的IQueryable<T>包装器是不够的,您必须创建自定义的IQueryProvider包装器,以确保在返回的包装后的IQueryable<T>上进行撰写将继续返回包装器,直到请求最终的IEnumerator<T>(或IEnumerator),并且返回的底层异步可枚举被上述方法隐藏。

这是上面的简化实现:

public static partial class Extensions
{
    public static IQueryable<T> ToQueryable<T>(this IQueryable<T> source)
        => new Queryable<T>(new QueryProvider(source.Provider), source.Expression);

    class Queryable<T> : IQueryable<T>
    {
        internal Queryable(IQueryProvider provider, Expression expression)
        {
            Provider = provider;
            Expression = expression;
        }
        public Type ElementType => typeof(T);
        public Expression Expression { get; }
        public IQueryProvider Provider { get; }
        public IEnumerator<T> GetEnumerator() => Provider.Execute<IEnumerable<T>>(Expression)
            .ToEnumerable().GetEnumerator();
        IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
    }

    class QueryProvider : IQueryProvider
    {
        private readonly IQueryProvider source;
        internal QueryProvider(IQueryProvider source) => this.source = source;
        public IQueryable CreateQuery(Expression expression)
        {
            var query = source.CreateQuery(expression);
            return (IQueryable)Activator.CreateInstance(
                typeof(Queryable<>).MakeGenericType(query.ElementType),
                this, query.Expression);
        }
        public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
            => new Queryable<TElement>(this, expression);
        public object Execute(Expression expression) => source.Execute(expression);
        public TResult Execute<TResult>(Expression expression) => source.Execute<TResult>(expression);
    }
}

查询提供程序的实现并不完全正确,因为它假设只有自定义Queryable<T>会调用Execute方法来创建IEnumerable<T>,而外部调用仅用于直接方法,例如CountFirstOrDefaultMax等,但它适用于这种情况。

此实现的另一个缺点是,所有EF Core特定的Queryable扩展都无法使用,如果OData $expand依赖于Include / ThenInclude之类的方法,则可能是一个问题/问题的解决者。但是要解决该问题,需要更复杂的实现,并深入EF Core内部。

话虽这么说,用法当然是:

return Ok(_carsDataModelContext.Cars.ToQueryable());

合并List <T>和List <Optional <T >> - java

鉴于: List<Integer> integers = new ArrayList<>(Arrays.asList( 10, 12 )); List<Optional<Integer>> optionalIntegers = Arrays.asList( Optional.of(5), Optional.em…

无法从ArrayList <String>转换为List <Comparable> - java

当我写下面的代码时,编译器说 无法从ArrayList<String>转换为List<Comparable>private List<Comparable> get(){ return new ArrayList<String>(); } 但是当我用通配符编写返回类型时,代码会编译。private List&l…

OpenShift构建错误:无法在多模块Maven Spring启动项目的父模块中导入子模块类 - java

我有一个使用spring的多模块Maven项目。通用模块类用作业务模块项目中的直接导入。我可以在本地PC上编译并成功运行它们。当我在OpenShift中部署相同的模块时,出现错误,无法在业务模块中导入通用模块类。项目结构可以总结如下:项目根 通用模块 src pom.xml 业务模块 src pom.xml pom.xml父POM:<?xml vers…

List <Dog>是List <Animal>的子类吗?为什么Java泛型不是隐式多态的? - java

我对Java泛型如何处理继承/多态感到困惑。假设以下层次结构-动物(父母)狗-猫(儿童)因此,假设我有一个方法doSomething(List<Animal> animals)。根据继承和多态性的所有规则,我假设List<Dog>是List<Animal>,而List<Cat>是List<Animal&g…

将谓词<T>转换为Func <T,bool> - c#

我有一个包含成员Predicate的类,希望在Linq表达式中使用该类:using System.Linq; class MyClass { public bool DoAllHaveSomeProperty() { return m_instrumentList.All(m_filterExpression); } private IEnumerable&…