web-dev-qa-db-fra.com

Objet nul ou vide lorsque la requête LINQ to Entities ne renvoie rien

Disons que j'ai une requête LINQ comme celle-ci:

application = CreditDatabase
                        .Applications
                        .Select(Mapper.Map<Application>) 
                        .Where(c => c.uID == urID)
                        .DefaultIfEmpty().First();

Il renvoie null si la requête LINQ renvoie un jeu de résultats vide. Est-ce correct". Vaut-il mieux retourner un objet vide? Si oui, comment puis-je faire cela? Ce lien indique qu'il n'est pas possible de renvoyer un type de référence non nul: https://msdn.Microsoft.com/en-us/library/bb360179 (v = vs.110) .aspx

3
w0051977

Les requêtes de base de données renvoient résultat définit. Un ensemble vide est une réponse raisonnable; cela signifie que vous n'avez aucune des choses recherchées.

Je dirais que votre code va trop loin dans la canonisation du résultat.

Votre code (quelque part peu après cette ligne) va vérifier si l'application que vous venez de rechercher existe ou non, donc vous n'empêchez pas la logique conditionnelle suivante (non affichée dans votre question) dans votre code en faisant .FirstOrDefault().

Vous pouvez - et je soutiens devrait - à la place: vérifier l'ensemble vide (c'est-à-dire utiliser la taille de l'ensemble de résultats, peut-être avec .Any()) pour voir si l'application existe. C'est beaucoup plus propre que de convertir l'ensemble vide par défaut, puis de prendre le premier et enfin de vérifier plus tard que null pour voir si l'application d'intérêt existe.

Plutôt que d'aller aussi loin:

application = CreditDatabase
                    .Applications
                    .Select(Mapper.Map<Application>) 
                    .Where(c => c.uID == urID)
                    .DefaultIfEmpty().First();
if ( application == null ) {
   // handle missing application, maybe add it...
} else {
    // use the found application
}

Omettez la gestion par défaut; au lieu:

search = CreditDatabase
                    .Applications
                    .Select(Mapper.Map<Application>) 
                    .Where(c => c.uID == urID);
if ( ! search.Any () ) {
    // handle missing application, maybe add it...
} else {
    application = search.Single ();
    // use the found application
}

Addendum: Dans la application = search.Single () ci-dessus, j'ai utilisé .Single() au lieu de .First() depuis sauf si l'ensemble de résultats non vide a plus d'une correspondance est logique ici: c'est une recherche par ce que nous supposons être une correspondance exacte de la clé primaire unique (sinon, comme @JacquesB le fait remarquer, puisque la requête n'est pas ordonnée, nous ne savons pas vraiment ce que nous '' re garder et ce que nous jetons avec .First()).

8
Erik Eidt

Cela dépend!

Mais d'abord une clarification: .DefaultIfEmpty().First() peut être écrit plus simplement comme .FirstOrDefault() qui fait de même - retourne le premier élément, ou null si le résultat est vide. Mais vous avez probablement un bogue ici: First() indique que vous pourriez avoir plusieurs articles et que vous souhaitez sélectionner le premier article - mais comme il n'y a pas de commande, cela signifie que vous obtiendrez un élément aléatoire . Ce n'est probablement pas ce que vous voulez! Si vous vous attendez vraiment à ce qu'il puisse y avoir plusieurs correspondances, renvoyez simplement la liste des correspondances. Si vous attendez un seul élément, utilisez à la place Single(), et si vous attendez un seul élément ou rien, utilisez SingleOrDefult() qui renverra également null s'il n'y en avait pas rencontre.

Passons maintenant à la question intéressante: que faut-il revenir d'une méthode qui trouve soit un seul objet, soit rien?

Il existe différentes options selon ce que vous souhaitez réaliser:

  • Renvoie null si l'élément n'est pas trouvé. Avantages: c'est vraiment simple. Inconvénients: Le client doit vérifier la valeur null qui est facilement oubliée car le système de type n'applique pas cette vérification. Cela peut conduire à un débogage difficile NullReferenceExceptions sur toute la ligne. Non recommandé.

  • Lance une exception si l'objet n'est pas trouvé. Avantages: simple et sûr. Inconvénients: ne doit être utilisé que s'il indique un bogue si l'objet n'existe pas.

  • Créez une méthode distincte qui vous permet de vérifier si l'objet existe (par exemple. if (ApplicationExists(id))...). Cette méthode renvoie un booléen. Avantages: Code clair côté client. Inconvénients: atteindra la base de données deux fois à moins que vous n'utilisiez une sorte de mise en cache, donc cela peut être coûteux. Aussi plus de code.

  • Utilisez le modèle TryXXX. Renvoie un booléen qui indique si l'objet existe et place l'objet (s'il est présent) dans un paramètre out. Avantages: Ce modèle est déjà connu de say Dictionary.TryGetValue() pour que les lecteurs ne soient pas confus. Inconvénient: le paramètre out conduit à un code quelque peu moche, bien qu'il soit amélioré en C # 6.

  • Retourne un Option<T> type. Cela nécessite que le client vérifie et déballe explicitement l'objet. Avantages: beaucoup plus sûr et plus élégant que l'utilisation de null. Inconvénient: C # n'a pas de fonction intégrée Option<T> tapez, vous devez en trouver un ou en écrire un vous-même.

La seule chose que vous ne devriez pas faire:

  • retourne une instance "vide" de la classe.
2
JacquesB

J'aime réaccorder null quand quelque chose n'est pas trouvé. Pour moi, cela a du sens.

Donnez-moi le premier article correspondant à ces critères .. il n'y en a pas, voici un null. Votre code lèvera une exception s'il essaie de l'utiliser.

Une alternative si vous n'aimez pas tester les valeurs nulles ou les objets vides, c'est de toujours renvoyer une liste d'objets.

De cette façon, vous pouvez faire un pour chacun sur la liste et cela ne fait naturellement rien si rien n'est trouvé.

Il n'y a aucun moyen de remplacer le comportement par défaut, mais vous pouvez utiliser la concaténation nulle

var x = Applications.FirstOrDefault() ?? new Application();

Ps. déplacez votre mappage vers après la clause where!

0
Ewan