2010-05-16 6 views
3

J'ai une userList, certains utilisateurs n'ont pas de nom (null). Si j'exécute la première requête LINQ, j'ai reçu une erreur indiquant que l'erreur "Référence d'objet n'est pas définie sur une instance d'un objet".La requête LINQ vérifie null

var temp = (from a in userList 
      where ((a.name == "john") && (a.name != null)) 
      select a).ToList(); 

Cependant, si je passe l'ordre en mettant la vérification des valeurs NULL devant, il fonctionne sans jeter aucune erreur:

var temp = (from a in userList 
      where ((a.name != null) && (a.name == "john")) 
      select a).ToList(); 

Pourquoi? Si c'est du code C# pur (pas LINQ), je pense que les deux seraient les mêmes. Je n'ai pas de profileur SQL, je suis juste curieux de savoir quelle sera la différence quand ils seront traduits au niveau SQL.

Répondre

10

En C#, le && operator court-circuite donc si la première condition renvoie la valeur false, la seconde condition n'est pas exécutée du tout. De MSDN:

The conditional-AND operator (&&) performs a logical-AND of its bool operands, but only evaluates its second operand if necessary.

Les || operator se comporte de la même manière, sauf qu'il n'évalue pas son second argument si les premiers retours vrai.


Je ne pense pas que ce soit l'histoire complète cependant. Le reste de mon article traite des points suivants:

  • Vous pouvez enregistrer les instructions SQL à l'aide de DataContext.Log.
  • Votre requête ne doit pas générer d'erreur quel que soit le chemin dans lequel vous l'écrivez.
  • Il existe des différences de comportement entre LINQ et les objets et LINQ vers SQL.
  • Votre filtrage peut s'exécuter localement plutôt que dans la base de données.

Vous pouvez facilement visualiser le SQL généré dans Visual Studio sans avoir besoin d'un profileur SQL. Vous pouvez passer votre souris sur un objet de requête LINQ to SQL et afficher le SQL. Ou vous pouvez utiliser le DataContext.Log pour enregistrer les instructions SQL, par exemple comme ceci:

TextWriter textWriter = new StringWriter(); 
using (var dc = new UserDataContext()) 
{ 
    dc.Log = textWriter; 
    var userList = dc.Users; 
    var temp = (from a in userList 
       where (a.Name.ToString() == "john") && (a.Name != null) 
       select a).ToList(); 
} 
string log = textWriter.ToString(); 

Vous pouvez également vous connecter à un fichier ou même Console.Out:

dc.Log = Console.Out; 

Ce faisant, vous pouvez voir que le requête ressemble à quelque chose comme ça, même si vous aurez probablement plus de colonnes dans la liste de sélection:

SELECT [t0].[Name] 
FROM [dbo].[User] AS [t0] 
WHERE ([t0].[Name] = @p0) AND ([t0].[Name] IS NOT NULL) 

un autre point est que votre requête ne devrait pas générer une erreur. Même si a.name est nul, a == "john" devrait encore fonctionner - il retournera juste faux. Enfin, il existe une différence entre le fonctionnement de C# et le fonctionnement de LINQ to SQL. Vous ne devriez pas obtenir une exception nulle de la base de données. Pour démontrer cela, je vais faire une petite modification à votre requête - l'ajout d'un ToString après a.Name:

var temp = (from a in userList 
      where (a.Name.ToString() == "john") && (a.Name != null) 
      select a).ToList(); 

Maintenant, cela ne fonctionne pas pour LINQ to Objects avec un NullReferenceException, mais il fonctionne avec LINQ to SQL sans lancer une exception. Je suppose donc que vous avez chargé tous les éléments de la base de données en mémoire et que vous filtrez localement.En d'autres termes, peut-être vous avez quelque chose comme ceci:

var userList = dc.Users.ToList(); 

au lieu des éléments suivants qui permettrait à la base de données pour faire le filtrage:

var userList = dc.Users; 

Je soupçonne qu'il ya plus à cette question que rencontre la œil. Peut-être que vous pouvez fournir plus de détails.

+0

Bonne réponse - doux et simple –

+0

Merci beaucoup ... – userb00

+0

@Preet Sangha : Pas si court maintenant. En y regardant de plus près, je ne suis pas sûr que la réponse simple explique complètement le comportement. –

0

En ce qui concerne la question de savoir comment cela traduit le SQL - je pense que SQL a la même sémantique de court-circuit, de sorte que le traducteur SQL préserve simplement l'ordre des conditions dans la requête générée.

Par exemple, les deux clauses suivantes: LINQ

where p.CategoryID != null && p.CategoryID.Value > 1 
where p.CategoryID.Value > 1 && p.CategoryID != null 

translet aux deux clauses SQL suivantes:

WHERE ([t0].[CategoryID] IS NOT NULL) AND (([t0].[CategoryID]) > @p0) 
WHERE (([t0].[CategoryID]) > @p0) AND ([t0].[CategoryID] IS NOT NULL) 
+0

Je ne pense pas que SQL ait la même sémantique de court-circuitage que C#. Voir cette question http://stackoverflow.com/questions/2842334/linq-query-checks-for-null/2842340#2842340 et ce post: http://weblogs.sqlteam.com/mladenp/archive/2008/02/ 25/How-SQL-Server-court-circuits-WHERE-condition-evaluation.aspx –

+0

@Mark: Intéressant - probablement cela n'a pas d'importance dans ce cas, car la requête SQL n'échoue pas quand on travaille avec 'null'. . –