简体   繁体   中英

Finding the Max value through GroupBy throws Sequence contains no elements exception

I have the following statement, where I use GroupBy to calculate the maximum value, and I check if there are any records beforehand using Any() but this query still returns Sequence contains no elements exception error. Any ideas?

baseQuery = baseQuery.Where(s => s.LearningActions.SelectMany(la => la.ProgressUpdates)
                     .GroupBy(d => d.LearningActionId)
                     .Select(a => a.Any() ? new { Max = a.Max(d => d.Progress) } : new { Max = 0})
                     .Average(d => d.Max) < averageProgress.GetValueOrDefault());

Just in case it is needed here is the baseQuery

 IQueryable<Submission> baseQuery = _context.Submissions                 
             .Where(s => s.ReviewRoundId == reviewRoundId);

I also tried the following as an alternative:

baseQuery = baseQuery.Where(s => s.LearningActions.SelectMany(la => la.ProgressUpdates)
                      .GroupBy(d => d.LearningActionId)
                      .Select(a => new { Max = (int?) a.Max(d => d.Progress) })
                      .Average(a => a.Max.GetValueOrDefault()) < averageProgress.GetValueOrDefault());

But it throws:

Query source (from LearningActionProgress d in [a]) has already been associated with an expression.

UPDATE

The following code worked:

 baseQuery = baseQuery.Where(s => s.LearningActions.SelectMany(la => la.ProgressUpdates)
                                                      .GroupBy(d => d.LearningActionId)
                                                      .Select(a => a.Max(d => d.Progress)).Any() ?
                                                      s.LearningActions.SelectMany(la => la.ProgressUpdates)
                                                      .GroupBy(d => d.LearningActionId)
                                                      .Select(a => a.Max(d => d.Progress)).Average() > averageProgress.GetValueOrDefault() : false); // Here a can be null or empty

I had to put this check s.LearningActions.SelectMany(la => la.ProgressUpdates).GroupBy(d => d.LearningActionId).Select(a => a.Max(d => d.Progress)).Any()

But, I see this a bit redundant, how can I include this check in a better way?

Your problem is the .Average(a => a.Max) . a can be null or empty. You have to check it:

IEnumerable<Submission> baseQuery = new List<Submission>()
    {
        new Submission()
            {
                LearningActions = new List<LearningAction>()
                    {
                        //new LearningAction() { ProgressUpdates = null }, // will throw nullRef
                        new LearningAction() { ProgressUpdates = new List<ProgressUpdate>() }, // this is your problem
                    },
            }
    };

int? averageProgress = 100;

baseQuery = baseQuery.Where(
    s => s.LearningActions.SelectMany(la => la.ProgressUpdates)
         .GroupBy(d => d.LearningActionId)
         .Select(a => a.Any() ? new { Max = a.Max(d => d.Progress) } : new { Max = 0 })
         .Average(a => a?.Max) < averageProgress.GetValueOrDefault()); // Here a can be null or empty
         // For Expressions use Any again:
         // .Average(a => a.Any() ? a : 0) < averageProgress.GetValueOrDefault());

Console.WriteLine(string.Join("\n\n", baseQuery));

If you want to use this code you could shorten it a little bit. You don't need the anonymous class:

baseQuery = baseQuery.Where(
    s => s.LearningActions.SelectMany(la => la.ProgressUpdates)
         .GroupBy(d => d.LearningActionId)
         .Select(a => a?.Max(d => d.Progress)) // select the "int?"
         .Average() < averageProgress.GetValueOrDefault()); // Average can be performed on the resulting IEnumerable<int?>

Can you create a reasonable default object for using DefaultIfEmpty?

MSDN: DefaultIfEmpty

baseQuery = baseQuery.Where(s => s.LearningActions.SelectMany(la => la.ProgressUpdates)
                     .GroupBy(d => d.LearningActionId)
                     .Select(a => a.Any() ? new { Max = a.Max(d => d.Progress) } : new { Max = 0})
                     .DefaultIfEmpty(defaultItem)
                     .Average(d => d.Max) < averageProgress.GetValueOrDefault());

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM