C# 许多嵌套的AggregateException

C# 许多嵌套的AggregateException,c#,async-await,entity-framework-core,aggregateexception,C#,Async Await,Entity Framework Core,Aggregateexception,在使用EntityFramework7时,我在一些linq上犯了一个简单的错误(使用了Skip并忘记包含我的OrderBy子句) 由此引发的异常包括许多嵌套聚合异常 生成(并捕获)异常的代码是: int[] newIds; try { newIds = await db.Products .Where(p => p.PortalId == portalId) .Skip(ids.ProductIds.Count) //Skip the rows al

在使用EntityFramework7时,我在一些linq上犯了一个简单的错误(使用了Skip并忘记包含我的OrderBy子句)

由此引发的异常包括许多嵌套聚合异常

生成(并捕获)异常的代码是:

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[] { };
}
上面的代码位于从Asp.NET5WebAPI控制器调用的repo类中。所有级别的调用都使用异步等待

然而,我从中得到的聚合异常是(这从上面显示的catch块转储到立即窗口):

System.AggregateException:发生一个或多个错误。--> System.AggregateException:发生一个或多个错误。--> System.AggregateException:发生一个或多个错误。--> System.AggregateException:发生一个或多个错误。--> System.AggregateException:发生一个或多个错误。--> System.AggregateException:发生一个或多个错误。--> System.InvalidOperationException:包含跳过运算符的查询 必须至少包含一个OrderBy操作。在 Microsoft.Data.Entity.Relational.Query.Sql.DefaultSqlQueryGenerator.GenerateLimitOffset(SelectExpression 选择表达式)在 Microsoft.Data.Entity.Relational.Query.Sql.DefaultSqlQueryGenerator.VisitSelectExpression(SelectExpression 选择表达式)在 Microsoft.Data.Entity.Relational.Query.Expressions.SelectExpression.Accept(ExpressionTreeVisitor 访客)在 Microsoft.Data.Entity.Relational.Query.Sql.DefaultSqlQueryGenerator.GenerateSql(SelectExpression selectExpression,IDictionary`2参数值) 等等

在这里,实际的异常被一大堆聚合异常层(6个嵌套层)包装起来。我理解为什么会出现聚合异常,但想知道为什么会有这么多异常?更重要的是,在异常冒泡回到控制器入口点之前,我正在查看它

这是由于AsyncWait的许多层造成的(我想我没有6层那么多),还是EF7实现中的一个问题

目前使用的是EF 7 7.0.0-beta4版。

如前所述,由
任务
引发的所有异常在被抛出到等待代码之前都被包装在
聚合异常
中。如果您正在使用多个级别的async/await,并且没有在尽可能低的级别捕获此异常,那么每次它冒泡到另一个级别时,它都会被重新包装,从而导致
AggregateException
内部
AggregateException
,每次等待时都有一个异常,而没有捕获

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

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


正如在评论中提醒我的那样,虽然您等待的东西返回任务,但它们也会为您进行一定程度的解包,因此等待的行为与任务的行为不同。结果,这意味着等待应该抛出实际的异常,而不将其包装在聚合异常中。这一切意味着我们最多只能得到一半的答案(这有点尴尬,因为它已经被接受了)。老实说,我建议你不要接受这个答案,这样其他人就不会跳过你的问题,看看其他人是否知道一些可以填补空白的东西。

这与链中调用的方法的数量无关。你只需要打电话给ArrayaSync

我认为问题出在Rx.NET上。我发送了一个请求来修复它:


我已经在我能达到的最低水平上捕捉到了它——在它退出EF代码时。这是否意味着EF内部有6个级别的等待?这是可能的。也可能是各种链式LINQ操作符都在单独等待。。。事实上,我要在我的答案中加上这一点。我用控制台应用程序做了进一步的测试,只执行了EF代码,同样的结果也发生了,所以看起来像是EF代码的内部结构在做我们已经达到的结果。我将在Github项目上提出它-这不是一个小交易,而是一个可用性问题。值得注意的是,
aggregateeexception
覆盖了
GetBaseException()
,特别是在这样的情况下:它将返回最内部的
aggregateeexception
,无论嵌套有多深。这允许您从该异常获取状态和上下文信息,然后检查其
InnerException
中抛出的错误-在本例中,您的
InvalidOperationException
。只需记住异步代码有可能返回此类异常,并始终使用
GetBaseException()
跳过所有繁琐的展开。
await
不会将其异常包装在
aggregateeexception
中。因此,
async
/
await
的层不会在每个级别添加包装器。此外,LINQ调用也不会添加这些包装器。