Ho una query che è necessario eseguire più di 300 cicli ogni singola chiamata. Ci vogliono circa 10 secondi per completare una chiamata anche su un nuovo database. Non è accettabile per una chiamata 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;
Come posso ottimizzare la velocità combinando tutte le query in una? Il contenuto del loop è letteralmente lo stesso eccetto il giorno. Ci sono 15 query complessive da chiamare su ogni ciclo, 4500 query db in un ciclo completo.
Pensa in termini di set e dati relazionali e non proceduralmente. Non è facile determinare esattamente ciò che vuoi dal tuo codice (che contraddice se stesso - queryable
è impostato da una chiamata a db.ObjectA.Where(...)
che è un IQueryable<ObjectA>
ma poi viene impostato anche da una chiamata a db.ObjectA.First(...)
, che è un ObjectA
; presumo che tu voglia IQueryable<ObjectA>
poiché i riferimenti di codice successivi sono queryable.Any(...)
) ma ecco la mia ipotesi:
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 ),
} );
I risultati sono un IQueryable
di un tipo anonimo che associa un "attivo" Operation.Day
con la logica per IsLogicACorrect
. Per gli altri IsLogicXCorrect
bandiere, se tutti possono essere determinate utilizzando ObjecetA
gruppo aoGroup
, è sufficiente aggiungerli al GroupJoin
selettore di risultato (come mostrato nella proprietà commentato-out). Se tali flag necessitano dei propri raggruppamenti (ad esempio, è necessario utilizzare il gruppo ObjectB
per determinare IsLogicBCorrect
, quindi aggiungere ulteriori chiamate a GroupJoin
come illustrato sopra ma utilizzando le rispettive DbSet
e proprietà. Ad esempio, se è necessario utilizzare db.ObjectB
per 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 ),
} );