【问题标题】:Entity Framework combining multiple queries into one实体框架将多个查询合二为一
【发布时间】:2018-10-04 22:58:36
【问题描述】:

我有一个查询,它需要在每次调用时运行 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;

如何通过将所有查询合二为一来优化速度?除了一天之外,循环内容实际上是相同的。每个循环总共要调用 15 个查询,一个完整的循环中有 4500 个 db 查询。

【问题讨论】:

  • 请多发些代码,你对continue;有什么逻辑吗?我们需要查看更多内容以确定可以更改的内容
  • 您应该首先获取给定日期范围内的所有活动操作,然后获取给定日期范围内的所有 ObjectA。获得该列表后,您可以从列表中查询,而不是每次都访问数据库。
  • 我又加了一些,不过大部分都是多余的,只是为了检查数据库本身是否有当天的资源。
  • 我确信逻辑可以大大优化,但是如果不查看真实代码,就不可能准确地评论如何最好地做到这一点。要优化 EF 查询,您需要考虑所涉及的实体、它们的关系以及您需要应用的所有计算/转换。
  • 停止写代码,用代码思考。你到底想达到什么目的?用简单的英语陈述它,然后考虑解决方案,看看它是否有意义。如果是这样,请开始为您的解决方案编写代码。现在看看你的代码:object 是什么?你没有给它分配任何东西,你正在尝试LogUtil.Info($"{object.Name}");,为什么会这样?查看问题出在您的解决方案上。如果您无法用简单的英语解释您的解决方案(算法),请不要尝试编写代码。

标签: c# entity-framework entity-framework-6 asp.net-web-api2


【解决方案1】:

从集合和关系数据的角度考虑,而不是从程序上考虑。从代码中准确确定您想要的内容并不容易(这自相矛盾 - queryable 是通过调用 db.ObjectA.Where(...) 设置的,这是一个 IQueryable&lt;ObjectA&gt;,但它也是通过调用 db.ObjectA.First(...) 设置的,它是ObjectA;我假设你想要IQueryable&lt;ObjectA&gt;,因为后面的代码引用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,它将“活动”Operation.Day 映射到您对IsLogicACorrect 的逻辑。对于您的其他IsLogicXCorrect 标志,如果它们都可以使用ObjecetAaoGroup 确定,只需将它们添加到GroupJoin 结果选择器(如注释掉的属性中所示)。如果这些标志需要自己的分组(例如,需要使用ObjectB 组来确定IsLogicBCorrect,然后如上所示添加对GroupJoin 的额外调用,但使用它们各自的DbSet 和属性。例如,如果您需要将db.ObjectB 用于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 ),
        } );

【讨论】:

    猜你喜欢
    • 2022-07-21
    • 1970-01-01
    • 2016-12-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多