Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

LINQ SelectMany and Where extension method ignoring nulls

I have the below example code, and I am interested to know how I can make this any cleaner, possibly through better use of SelectMany(). At this point the QuestionList property will not be null. All I want is a list of answerRows that are not null, but Questions can sometimes be null too.

IEnumerable<IQuestion> questions = survey.QuestionList                     .Where(q => q.Questions != null)                     .SelectMany(q => q.Questions);              if(questions == null) return null;  IEnumerable<IAnswerRow> answerRows = questions                     .Where(q => q.AnswerRows != null)                     .SelectMany(q => q.AnswerRows);  if(answerRows == null) return null; 

I was interested by Jon's comment about Enumerable.SelectMany and Null.. so I wanted to try my example with some fake data to more easily see where the error is, please see the below, specifically how I am using SelectMany() on the result of a SelectMany(), its clearer to me now that the problem was having to make sure you don't use SelectMany() on a null reference, obvious when I actually read the NullReferenceException name :( and finally put things together.

Also while doing this, I realised that the use of try { } catch() { } in this example is useless and as usual Jon Skeet has the answer :) deferred execution..

so if you want to see the exception for row 2, comment out the relevant row 1 bits :P, sorry I couldn't figure out how to stop this error without re-writing the code example.

using System; using System.Collections.Generic; using System.Linq;  namespace SelectManyExample {     class Program     {         static void Main(string[] args)         {             var questionGroupList1 = new List<QuestionGroup>() {                 new QuestionGroup() {                     Questions = new List<Question>() {                         new Question() {                             AnswerRows = new List<AnswerRow>() {                                 new AnswerRow(),                                 new AnswerRow()                             }                         },                          // empty question, causes cascading SelectMany to throw a NullReferenceException                         null,                          new Question() {                             AnswerRows = new List<AnswerRow>() {                                 new AnswerRow() {                                     Answers = new List<Answer>() {                                         new Answer(),                                         new Answer()                                     }                                 }                             }                         }                     }                 }             };              var questionGroupList2 = new List<QuestionGroup>() {                 null,                 new QuestionGroup()             };              IEnumerable<AnswerRow> answerRows1 = null;             IEnumerable<AnswerRow> answerRows2 = null;              try             {                 answerRows1 = questionGroupList1                     .SelectMany(q => q.Questions)                     .SelectMany(q => q.AnswerRows);             }             catch(Exception e) {                 Console.WriteLine("row 1 error = " + e.Message);             }              try             {                 answerRows2 = questionGroupList2                     .SelectMany(q => q.Questions)                     .SelectMany(q => q.AnswerRows);             }             catch (Exception e)             {                 Console.WriteLine("row 2 error = " + e.Message);             }               Console.WriteLine("row 1: " + answerRows1.Count());             Console.WriteLine("row 2: " + answerRows2.Count());             Console.ReadLine();         }       }      public class QuestionGroup {         public IEnumerable<Question> Questions { get; set; }     }      public class Question {         public IEnumerable<AnswerRow> AnswerRows { get; set; }     }      public class AnswerRow {         public IEnumerable<Answer> Answers { get; set; }     }      public class Answer {         public string Name { get; set; }     } } 
like image 952
Pricey Avatar asked Jan 22 '13 22:01

Pricey


1 Answers

survey.QuestionList     .Where(l => l.Questions != null)     .SelectMany(l => l.Questions)     .Where(q => q != null && q.AnswerRows != null)     .SelectMany(q => q.AnswerRows); 

I'd recommend you ensure your collections are never null. null can be a bit of a nuisance if you don't handle it well. You end up with if (something != null) {} all over your code. Then use:

survey.QuestionList     .SelectMany(l => l.Questions)     .SelectMany(q => q.AnswerRows); 
like image 158
Paul Fleming Avatar answered Sep 21 '22 08:09

Paul Fleming