EF Core不必要地执行条件分配逻辑的两个分支

时间:2018-09-19 23:00:20

标签: c# linq entity-framework-core

我遇到了EF Core的问题,如果我使用分支逻辑分配属性,则两个分支都将执行(而不仅仅是条件== true的分支)。

在下面的第一个代码示例中,我将实际要测试的条件替换为我知道始终为假的条件以验证我的问题。在我的实际示例中,MyEntities映射到的表没有Id高达1000的记录。

但是,当我调用此方法时,我首先可以在输出窗口中看到查询,以获取Id为1000的位置(不返回任何结果),然后是N个SQL查询尝试检索{ {1}}在第一个分支的LINQ管道的末尾被执行。如果条件始终为假,则永远不要执行这些获取Count的查询(应该只执行另一个Count分支),对吗?

我怀疑这可能是EF Core或我的EF Core Provider(Pomelo MySQL)的错误,但是我想确保我不会误解EF和/或LINQ中的分支的工作方式。两个分支都不应该有条件地执行,对吗?如果您认为这可能是一个错误,那么我怎么知道它是否与EF Core或我使用的提供程序有关?

(int?)null

如果每个public async Task<IEnumerable<MyEntityDto>> GetMyEntityDtosAsync() => await DbContext.MyEntities .Select(l => new MyEntityDto { Id = l.Id, RuleCount = l.Id == 1000 // known to always be false ? l.MyLinkedEntity .Where(s => s.MyBoolean) .FirstOrDefault() .MyManyToMany .Count : (int?)null }); MyEntity中没有MyLinkedEntity,则我尝试实现的实际查询看起来更像下面,从而可以正常处理。如果没有任何MyBoolean == true满足这些条件(假定由条件处理),则会引发异常。有没有可能我可以尝试的解决方法?

MyLinkedEntity

更新:添加POCO

public async Task<IEnumerable<MyEntityDto>> GetMyEntityDtosAsync() =>
    await DbContext.MyEntities
                   .Select(l => new MyEntityDto
                   {
                       Id = l.Id,
                       RuleCount = l.MyLinkedEntity.Any(s => s.MyBoolean)
                           ? l.MyLinkedEntity
                              .Where(s => s.MyBoolean)
                              .FirstOrDefault()
                              .MyManyToMany
                              .Count
                           : (int?)null
                   });

1 个答案:

答案 0 :(得分:1)

这是我的猜测:EF正在检索可能是最终预测一部分的所有数据,然后用结果评估您的条件逻辑客户端。

您收到的错误是由于您使用MyLinkedEntity.MyManyToMany.Count返回的空引用来引用FirstOrDefault()

由于您尚未发布POCO,因此我使用了它,但是在当前代码中使用l.MyLinkedEntity类型的实体集,请按主体(从中进行投影的实体)分组和项目ManyToMany.Count 之前调用FirstOrDefault()。希望这行得通,我不在家测试:P

DbContext.MyLinkedEntities
    .GroupBy( mle => mle.MyEntity )
    .Select( g => new
    {
        Id = g.Key.Id,
        RuleCount = g.Where( s => s.MyBoolean )
            .Select( s => ( int? )s.ManyToMany.Count )
            .FirstOrDefault()
    }