c# - Complexity limits of Linq queries -


i'm big fan of linq, , have been enjoying power of expression trees etc. have found whenever try clever queries, hit kind of limitation in framework: while query can take short time run on database (as shown performance analyzer), results take ages materialize. when happens know i've been fancy, , start breaking query smaller, bite sized chunks - have solution that, though might not optimal.

but i'd understand:

  • what pushes linq framework on edge in terms of materializing query results?
  • where can read mechanism of materializing query results?
  • is there measurable complexity limit linq queries should avoided?
  • what design patterns known cause problem, , patterns can remedy it?

edit: requested in comments, here's example of query measured run on sql server in few seconds, took 2 minutes materialize. i'm not going try explaining stuff in context; it's here can view constructs , see example of i'm talking about:

expression<func<staff, teacherinfo>> teacherinfo =     st => new teacherinfo     {         id = st.id,         name = st.firstname + " " + st.lastname,         email = st.email,         phone = st.telmobile,     };  var step1 =     currentreportcards.asexpandable()         .groupjoin(db.scholarreportcards,                              current =>                              new { current.scholarid, current.academicterm.academicyearid },                              past => new { past.scholarid, past.academicterm.academicyearid },                              (current, past) => new                              {                                  current = current,                                  pastcards =                                  past.where(                                      rc =>                                      rc.academicterm.startdate <                                      current.academicterm.startdate &&                                      rc.academicterm.grade == current.academicterm.grade &&                                      rc.academicterm.schoolid == current.academicterm.schoolid)                              }); // materialization takes long time: var subjects = step1.selectmany(x => key in x.current.subjects                             .select(s => new { s.subject.subjectid, s.subject.subjectcategoryid })                             .union(x.pastcards.selectmany(c => c.subjects)                                             .select(                                                 s => new { s.subject.subjectid, s.subject.subjectcategoryid }))          join cur in x.current.subjects on key equals              new { cur.subject.subjectid, cur.subject.subjectcategoryid } jcur          cur in jcur.defaultifempty()          join past in x.pastcards.selectmany(p => p.subjects) on key equals              new { past.subject.subjectid, past.subject.subjectcategoryid } past          select new          {              x.current.scholarid,              includeincontactsection =                  // resharper disable constantnullcoalescingcondition                 (bool?)cur.subject.includeincontactsection ?? false,              includegrades = (bool?)cur.subject.includegrades ?? true,              // resharper restore constantnullcoalescingcondition              subjectname =                 cur.subject.subject.name ?? past.firstordefault().subject.subject.name,              subjectcategoryname = cur.subject.subjectcategory.description,              classinfo = (from ce in mydb.classenrollments                              .where(                                  ce =>                                  ce.class.subjectid == cur.subject.subjectid                                  && ce.scholarid == x.current.scholarid)                              .where(enrollmentexpr)                              .orderbydescending(ce => ce.terminationdate ?? datetime.today)                      let teacher = ce.class.teacher                      let secteachers = ce.class.secondaryteachers                      select new                      {                          ce.class.nickname,                          primary = teacherinfo.invoke(teacher),                          secondaries = secteachers.asqueryable().asexpandable()                             .select(ti => teacherinfo.invoke(ti))                      })                 .firstordefault(),              comments = cur.comments                  .select(cc => new                  {                      staff = cc.staff.firstname + " "                                      + cc.staff.lastname,                      comment = cc.commenttemplate.text ??                                          cc.commentfreetext                  }),              // resharper disable constantnullcoalescingcondition              displayorder = (byte?)cur.subject.displayorder ?? (byte)99,              // resharper restore constantnullcoalescingcondition              cur.percentile,              cur.score,              cur.symbol,              cur.masterylevel,              pastscores = past.select(p => new                 {                     p.score,                     p.symbol,                     p.masterylevel,                     p.scholarreportcard                      .academictermid                 }),              assessments = cur.assessments                  .select(a => new                  {                      a.scholarassessment.assessmentid,                      a.scholarassessment.assessment.description,                      a.scholarassessment.assessment.type.nickname,                      a.scholarassessment.assessmentdate,                      a.scoredesc,                      a.scoreperc,                      a.masterylevel,                      a.scholarassessment.assessment.type.assessmentformat,                      a.scholarassessment.publishedstatus,                      a.scholarassessment.fpscore,                      a.scholarassessment.totalscore,                      a.scholarassessment.assessment.type.scoretype,                      a.scholarassessment.assessment.type.overridebelowlabel,                      a.scholarassessment.assessment.type.overrideapproachinglabel,                      a.scholarassessment.assessment.type.overridemeetinglabel,                      a.scholarassessment.assessment.type.overrideexceedinglabel,                  })             })         .tolist(); 

linq uses deferred execution tasks, example while iterating through ienumerable<>, call materialization includes actual data fetching.

var reportcards = db.scholarreportcards.where(cr => ...); // prepares query  foreach (var rc in reportcards) {} // executes query , calls db 

i think if trace/time queries on sql server may see queries arriving during "materialization" step. problem may exacerbated anti-patterns such the "select n+1" problem : example looks you're not including academicterm objects in request; if don't resolving these result in select n+1, every scholarreportcard there call db lazily resolve academicterm attached.

if focus on linq db aspect, @ least try not :

  • select n+1: include related datatables need
  • select data: include columns need in selection (include on table need)

Comments

Popular posts from this blog

java - WrongTypeOfReturnValue exception thrown when unit testing using mockito -

php - Magento - Deleted Base url key -

android - How to disable Button if EditText is empty ? -