web-dev-qa-db-fra.com

Méthode d'extension LINQ SelectMany et Where ignorant les valeurs NULL

J'ai l'exemple de code ci-dessous, et je suis intéressé de savoir comment je peux le rendre plus propre, éventuellement grâce à une meilleure utilisation de SelectMany(). À ce stade, la propriété QuestionList ne sera pas nulle. Tout ce que je veux, c'est une liste de answerRows qui ne soient pas nuls, mais Questions peut aussi parfois être nulle.

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;

MISE À JOUR: J'ai légèrement changé mon code car mon exemple n'était pas assez clair avec l'utilisation de var

La question était plus pour m'aider à en savoir plus sur l'utilisation de LINQ.

MISE À JOUR 2:

J'ai été intéressé par le commentaire de Jon sur Enumerable.SelectMany Et Null .. donc je voulais essayer mon exemple avec de fausses données pour voir plus facilement où est l'erreur, veuillez voir ci-dessous, en particulier comment j'utilise la fonction SelectMany() sur le résultat d'une SelectMany(), c'est plus clair pour moi maintenant que le problème était de s'assurer que vous n'utilisez pas SelectMany() sur une référence nulle, évident quand je lire en fait le NullReferenceException nom :( et enfin mettre les choses ensemble.

Également en faisant cela, j'ai réalisé que l'utilisation de try { } catch() { } dans cet exemple est inutile et comme d'habitude Jon Skeet a réponse :) exécution différée ..

donc si vous voulez voir l'exception pour la ligne 2, commentez les bits de la ligne 1 pertinents: P, désolé, je n'ai pas pu trouver comment arrêter cette erreur sans réécrire l'exemple de code.

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; }
    }
}
26
Pricey
survey.QuestionList
    .Where(l => l.Questions != null)
    .SelectMany(l => l.Questions)
    .Where(q => q != null && q.AnswerRows != null)
    .SelectMany(q => q.AnswerRows);

Je vous recommande de vous assurer que vos collections ne sont jamais null. null peut être un peu gênant si vous ne le gérez pas bien. Vous vous retrouvez avec if (something != null) {} partout dans votre code. Utilisez ensuite:

survey.QuestionList
    .SelectMany(l => l.Questions)
    .SelectMany(q => q.AnswerRows);
41
Paul Fleming
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);
}

Cela vous permet ensuite d'effectuer les opérations suivantes:

var allAnswers = survey.QuestionList
    .SelectNotNull(list => list.Questions)
    .SelectNotNull(question => question.AnswerRows);
8
Servy

Une solution conforme à DRY consisterait à utiliser l'opérateur de coalescence nulle ?? dans votre expression lambda SelectMany.

IEnumerable<IQuestion> questions = survey.QuestionList.SelectMany(q => q.Questions ?? Enumerable.Empty<IQuestion>());

IEnumerable<IAnswerRow> answerRows = questions.SelectMany(q => q.AnswerRows ?? Enumerable.Empty<IAnswerRow>());

Dans le code de l'OP et dans le code ci-dessus, questions et answerRows ne seront jamais nuls, donc les vérifications nulles ne sont pas requises (vous souhaiterez peut-être mettre .Any() vérifications en fonction sur votre logique métier). Mais le code ci-dessus n'entraînera également jamais d'exception si q.Questions Ou q.AnswerRows Est nul.

0
Neo