2013-01-22 7 views
13

Ho il seguente codice di esempio, e sono interessato a sapere come posso rendere questo qualsiasi pulitore, possibilmente attraverso un migliore uso di SelectMany(). A questo punto la proprietà QuestionList non sarà null. Tutto quello che voglio è un elenco di answerRows che non sia nullo, ma a volte anche lo Questions può essere nullo.LINQ SelectMany e Where metodo di estensione che ignora i valori null

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; 

UPDATE: cambiato il mio codice di un po 'perché il mio esempio non era abbastanza chiaro con l'uso di var

La domanda era più per avermi aiutato a conoscere meglio l'uso di LINQ.

UPDATE 2:

ero interessato dal commento di Jon su Enumerable.SelectMany e Null .. così ho voluto provare il mio esempio con alcuni dati falsi per vedere più facilmente dove l'errore è, si prega di vedere il seguito , in particolare come sto usando SelectMany() sul risultato di un SelectMany(), per me è più chiaro ora che il problema era quello di assicurarmi di non usare SelectMany() su un riferimento nullo, ovvio quando ho effettivamente letto il nome NullReferenceException :(e infine mettere le cose insieme

Anche mentre doin g questo, mi sono reso conto che l'uso di try { } catch() { } in questo esempio è inutile e come al solito Jon Skeet ha l'esecuzione differita answer :)

quindi se si desidera vedere l'eccezione per la riga 2, commentare la riga pertinente 1 bit: P, scusate non ho potuto capire come fermare questo errore senza riscrivere l'esempio di codice.

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; } 
    } 
} 
+1

Perché pensi che le vostre collezioni sarebbero mai essere nullo? –

+3

'questions' e' answerRows' non possono mai essere 'null'. E in un design sano, 'q.Questions' e' q.AnswerRows' probabilmente non dovrebbero mai essere 'nulli' pure. – Jon

+0

a volte 'se (! Question.HasAnswer) restituisce;' – spajce

risposta

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

vi consiglio di garantire le vostre collezioni non sono mai null. null può essere un po 'fastidioso se non lo gestisci bene. Finisci con if (something != null) {} in tutto il tuo codice. Quindi utilizzare:

survey.QuestionList 
    .SelectMany(l => l.Questions) 
    .SelectMany(q => q.AnswerRows); 
+0

Grazie, ho risposto alla mia domanda .. e sì a tutti quelli che leggono questo .. evita NULL quando puoi. – Pricey

+1

@Pricey. Piacere. Questo è un bell'esempio di "non puoi sempre fidarti dei voti"! :) –

+0

@Pricey. Non dimenticare di contrassegnare come corretto :) –

7
public static IEnumerable<TResult> SelectNotNull<TSource, TResult>(
    this IEnumerable<TSource> source, Func<TSource, IEnumerable<TResult>> selector) 
    where TResult : class 
{ 
    return source.Select(selector) 
     .Where(sequence => sequence != null) 
     .SelectMany(x => x) 
     .Where(item => item != null); 
} 

Questo quindi permette di effettuare le seguenti operazioni:

var allAnswers = survey.QuestionList 
    .SelectNotNull(list => list.Questions) 
    .SelectNotNull(question => question.AnswerRows); 
+0

Non è affatto un brutto modo. – MuhammadHani

+0

+1 grazie per l'esempio di estensione che è molto utile. – Pricey

+0

"dove TResult: class" - è una limitazione non necessaria che impedisce l'utilizzo di sequenze lunghe e int. Anche l'ultimo "dove" non è necessario, se necessario è possibile aggiungerlo "all'esterno". – greatvovan