3

Working with Entity Framework 7, I made a simple mistake with some linq (used Skip and forgot to include my OrderBy clause).

The exception that was thrown by this included a number of nested aggregate exceptions.

The code that generates (and catches) the exception is:

int[] newIds;
try
{
    newIds = await db.Products
        .Where(p => p.PortalId == portalId)
        .Skip(ids.ProductIds.Count) //Skip the rows already read
        .Take(takeTotal) //get the next block
        .Select(p => p.ProductId)
        .ToArrayAsync();
}
catch (AggregateException ex)
{
    Console.WriteLine(ex.Message);
    newIds = new int[] { };
}

The code above is in a repo class called from a Asp.Net 5 WebApi controller. All levels of the call are using async-await.

However the aggregate exception that I got from this was (this is dumped to the immediate window from the catch block shown above):

System.AggregateException: One or more errors occurred. ---> System.AggregateException: One or more errors occurred. ---> System.AggregateException: One or more errors occurred. ---> System.AggregateException: One or more errors occurred. ---> System.AggregateException: One or more errors occurred. ---> System.AggregateException: One or more errors occurred. ---> System.InvalidOperationException: A query containing the Skip operator must include at least one OrderBy operation. at Microsoft.Data.Entity.Relational.Query.Sql.DefaultSqlQueryGenerator.GenerateLimitOffset(SelectExpression selectExpression) at Microsoft.Data.Entity.Relational.Query.Sql.DefaultSqlQueryGenerator.VisitSelectExpression(SelectExpression selectExpression) at Microsoft.Data.Entity.Relational.Query.Expressions.SelectExpression.Accept(ExpressionTreeVisitor visitor) at Microsoft.Data.Entity.Relational.Query.Sql.DefaultSqlQueryGenerator.GenerateSql(SelectExpression selectExpression, IDictionary`2 parameterValues) etc etc

Here the actual exception has ended up wrapped by a whole bunch of layers of aggregate exception (6 nested layers). I understand why I'm getting an aggregate exception, but wondered why so many of them? More so since I'm looking at the exception before it has bubbled back up to the controller entry-point.

Would this be a result of a number of layers of async-await, (don't think I have as many as 6) or could it be an issue in the EF7 implementation?

This is currently using EF 7 release 7.0.0-beta4.

4

2 回答 2

4

正如MSDN 页面上Task<T>解释的那样,所有由 a 引发的异常都在被抛出到等待代码之前被Task包装。AggregateException如果您正在使用多个级别的 async/await 并且没有在可能的最低级别捕获此异常,那么每次它冒泡到另一个级别时,它都会再次被包裹,导致AggregateExceptioninside AggregateException,每次您等待时都有一个没有抓住。

也可能是每个操作都被算作自己的任务;IE。每次添加另一个操作时,结果都会从前一个操作出来并返回到下一个操作,每个操作都在等待上一个操作。看一看:

newIds = await db.Products               // 1
    .Where(p => p.PortalId == portalId)  // 2
    .Skip(ids.ProductIds.Count)          // 3
    .Take(takeTotal)                     // 4
    .Select(p => p.ProductId)            // 5
    .ToArrayAsync();                     // 6

六层东西,每一层都在等待上一层的结果。六层AggregateException。现在,您的异常是由六个中的第三个引起的,但从错误的性质来看,它很可能来自 EF 在执行任何操作之前读取您的整个查询的部分,并且在这样做时发现您有一个.Skip()没有匹配的.OrderBy()

正如斯蒂芬克利里在评论中提醒我的那样,当你await返回的东西时Task<T>,它们也会为你做了一定程度的解包,所以await表现得不太像Task<T>.Result,这意味着await 应该抛出实际的异常而不将其包装在AggregateException. 这一切意味着我们最多只有一半的答案(这有点尴尬,因为它已经被接受了)。老实说,我建议你不要接受这个答案,这样其他人就不会跳过你的问题,看看是否有其他人知道可能填补空白的东西。

于 2015-05-14T09:24:18.797 回答
0

它与链中调用的方法的数量无关。您只需要调用 ToArrayAsync。

我认为问题出在 Rx.NET 中。我发送了一个拉取请求来修复它:https ://github.com/aspnet/EntityFramework/issues/2192

https://github.com/Reactive-Extensions/Rx.NET/pull/131/files

于 2015-09-04T03:59:30.873 回答