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
标志,如果它们都可以使用 ObjecetA
组 aoGroup
确定,只需将它们添加到 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 ),
} );
我有一个查询,每次调用都需要 运行 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
标志,如果它们都可以使用 ObjecetA
组 aoGroup
确定,只需将它们添加到 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 ),
} );