Question

I have this nested for loop which I'd like to elminate by modifying the original query:

foreach (var record in records)
{    
  foreach (var ap in record.approverList)
  {
    var approval = (from c in dataContext.approvals
                    where c.recordId == record.recordId
                    && c.personId == ap.personId
                    orderby c.modified descending
                    select c).FirstOrDefault();

    if (approval != null)
    {
      ap.vote = approval.vote;
    }

  }

}

Edit

Ideally, I want to modify the original query for records, which looks something like this:

var records = (
  from record in dataContext.Records
  where record.statusId == RecordStatus.Submitted
).Include(e => e.approverList.Select(d => d.approver))
.ToList()
Était-ce utile?

La solution

So the real issue here is that you're executing a query for every single approval in every single record. You don't want to be doing that. What you want to be doing is joining your records on the database side with your approvals to create a query of pairs. Selecting out just the one matching approval from all of the joined approvals can be done with just a simple let.

Once you've queried all of the approvals and the approval list from the record that pairs with it, updating each item is simple enough.

var query = from record in records
            from recordApproval in record.approverList
            join approval in approvals
            on new { record.recordId, recordApproval.personId } equals
            new { approval.recordId, approval.personId }
            into approvalMatches
            where approvalMatches.Any()
            let approval = approvalMatches.OrderByDescending(a => a.modified)
                .FirstOrDefault()
            select new
            {
                recordApproval,
                approval,
            };

foreach (var result in query)
    result.recordApproval.vote = result.approval.vote;

Additionally it's worth noting that you shouldn't materialize records into a result set; you should keep it as an unmaterialized query so that the join can happen on the database site of things.

Autres conseils

Looking at your code the desired logic seems to be:

For each approval person of each record, get their latest vote.

The clearest way (readable/maintainable) to accomplish that, and with only one call to the database, would look something like this:

Note: approvals.recordId should be foreign keyed to record.recordId, or the code is slightly more complex (would require an explicit join)

// get all the latest votes for submitted records
var votes = dataContext.approvals
    .Where(x => x.record.statusId == RecordStatus.Submitted)
    .OrderByDescending(x => x.modified) // only the latest vote counts, per person
    .GroupBy(x => new { x.recordID, x.personID })
    .Select(x => x.First())
    .ToList();

After that, you can utilize the list like so:

// this lists all the distinct records
var recordIds = votes.Select(x => x.recordId).Distinct();

// see the votes for each record
foreach(var recordId in recordIds)
{
     var thisRecordsVotes = votes.Where(x => x.recordId = recordID).ToList();
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top