Entity Framework 将多个查询合并为一个

Entity Framework combining multiple queries into one

我有一个查询,每次调用都需要 运行 300 多个循环。即使在新数据库上完成调用也需要大约 10 秒。对于 WebAPI 调用来说,这是不可接受的。

var isAbnormal = false;
var list = new List<String>();

//Check date range, log them & return if there is any abnormal.
foreach (DateTime day in DateHelper.EachDay(startDate, endDate))
{
  var isActive = db.Operations.Any(x=>x.IsActive && x.Day == day);
  var object;
  var queryable = db.ObjectA.Where(x=>x.Day == day);

  if(isActive){
    queryable = db.ObjectA.First(x=>x.Day == day);

  LogUtil.Info($"{object.Name}");

  var isLogicACorrect = queryable.Any(x=>x.ObjectACount == 5);
  var isLogicBCorrect = queryable.Any(x=>x.ObjectBCount == 3);
  var isLogicCCorrect = queryable.Any(x=>x.ObjectCCount == 2);
  var isLogicDCorrect = queryable.Any(x=>x.ObjectDCount == 8);
  var isLogicECorrect = queryable.Any(x=>x.ObjectECount == 1);
  if(!isLogicACorrect){
    list.Add("Logic A is incorrect");
    isAbnormal = true;
    }

  //More logic codes & db calls here, which is just to select & validate.

}
return list;

如何通过将所有查询合并为一个来优化速度?除了 day.There 之外,循环内容几乎相同,每个循环总共调用 15 个查询,一个完整循环中有 4500 个数据库查询。

根据集合和关系数据而不是程序来思考。要从代码中准确确定您想要什么并不容易(这自相矛盾 - queryable 是通过调用 db.ObjectA.Where(...) 设置的,它是一个 IQueryable<ObjectA> 但它也是通过调用设置的到 db.ObjectA.First(...),这是一个 ObjectA;我假设你想要 IQueryable<ObjectA> 因为后面的代码引用 queryable.Any(...)) 但这是我的猜测:

var days = DateHelper.EachDay( startDate, endDate );

var activeDaysIsLogicCorrectFlags = db.Operations
    // get days that are "active"
    .Where( op => op.IsActive && days.Contains( op.Day ) )
    // join with ObjectA's to filter for active ObjectA's
    // is there a nav property you could use instead?
    // use GroupJoin for use with `Any(...)` in results
    .GroupJoin( db.ObjectA, op => op.Day, oa => oa.Day, ( op, oaGroup ) => new
        {
            //Operation = op,
            // projecting Operation.Day since that's what your foreach loop is using
            Day = op.Day,
            IsLogicACorrect = oaGroup.Any( oa => oa.ObjectACount == 5 ),
            // if IsLogicBCorrect can be determined from the collection of ObjectA's:
            //IsLogicBCorrect = oaGroup.Any( oa => oa.ObjectBCount == 3 ),
        } );

结果是匿名类型的 IQueryable,它将 "active" Operation.Day 映射到 IsLogicACorrect 的逻辑。对于其他 IsLogicXCorrect 标志,如果它们都可以使用 ObjecetAaoGroup 确定,只需将它们添加到 GroupJoin 结果选择器(如注释掉的部分所示) 属性)。如果这些标志需要它们自己的分组(例如需要使用 ObjectB 组来确定 IsLogicBCorrect,则如上所示添加对 GroupJoin 的额外调用,但使用它们各自的 DbSet 和属性. 例如,如果你需要使用 db.ObjectB for IsLogicBCorrect:

var activeDaysIsLogicCorrectFlags =
    <existing logic from above>
    .GroupJoin( db.ObjectB, at => at.Day, ob => ob.Day, ( at, obGroup ) => new
        {
            // project all previous results
            at.Day,
            at.IsLogicACorrect,
            // new flag
            IsLogicBCorrecet = obGroup.Any( ob => ob.ObjectBCount == 3 ),
        } );