40 votes

Comparez les types nullables dans Linq à SQL

J'ai une entité de catégorie qui a un champ Nullable ParentId. Lorsque la méthode ci-dessous est en cours d'exécution et que categoryId est null, le résultat semble nul, mais il existe des catégories qui ont une valeur ParentId nulle.

Quel est le problème ici, qu'est-ce qui me manque?

 public IEnumerable<ICategory> GetSubCategories(long? categoryId)
{
    var subCategories = this.Repository.Categories.Where(c => c.ParentId == categoryId)
        .ToList().Cast<ICategory>();

    return subCategories;
}
 

En passant, lorsque je change la condition en (c.ParentId == null), le résultat semble normal.

53voto

ariel Points 5372

Autre moyen:

 Where object.Equals(c.ParentId, categoryId)
 

ou

 Where (categoryId == null ? c.ParentId == null : c.ParentId == categoryId)
 

29voto

Marc Gravell Points 482669

La première chose à faire est de mettre sur la journalisation, pour voir ce que TSQL a été généré; par exemple:

 ctx.Log = Console.Out;
 

LINQ-to-SQL semble traiter les valeurs NULL de manière peu cohérente (en fonction de la valeur littérale ou valeur):

 using(var ctx = new DataClasses2DataContext())
{
    ctx.Log = Console.Out;
    int? mgr = (int?)null; // redundant int? for comparison...
    // 23 rows:
    var bosses1 = ctx.Employees.Where(x => x.ReportsTo == (int?)null).ToList();
    // 0 rows:
    var bosses2 = ctx.Employees.Where(x => x.ReportsTo == mgr).ToList();
}
 

Donc, tout ce que je peux suggérer, c'est d'utiliser la forme supérieure avec des valeurs nulles!

c'est à dire

 Expression<Func<Category,bool>> predicate;
if(categoryId == null) {
    predicate = c=>c.ParentId == null;
} else {
    predicate = c=>c.ParentId == categoryId;
}
var subCategories = this.Repository.Categories
           .Where(predicate).ToList().Cast<ICategory>();
 


Mise à jour - Je l’ai fonctionné "correctement" en utilisant un Expression :

     static void Main()
    {
        ShowEmps(29); // 4 rows
        ShowEmps(null); // 23 rows
    }
    static void ShowEmps(int? manager)
    {
        using (var ctx = new DataClasses2DataContext())
        {
            ctx.Log = Console.Out;
            var emps = ctx.Employees.Where(x => x.ReportsTo, manager).ToList();
            Console.WriteLine(emps.Count);
        }
    }
    static IQueryable<T> Where<T, TValue>(
        this IQueryable<T> source,
        Expression<Func<T, TValue?>> selector,
        TValue? value) where TValue : struct
    {
        var param = Expression.Parameter(typeof (T), "x");
        var member = Expression.Invoke(selector, param);
        var body = Expression.Equal(
                member, Expression.Constant(value, typeof (TValue?)));
        var lambda = Expression.Lambda<Func<T,bool>>(body, param);
        return source.Where(lambda);
    }
 

5voto

algreat Points 2494

Vous devez utiliser l'opérateur est Égal à:

 var subCategories = this.Repository.Categories.Where(c => c.ParentId.Equals(categoryId))
        .ToList().Cast<ICategory>();

Est égal à fot types nullables renvoie vrai si:

  • La propriété HasValue est faux, et l'autre paramètre est null. C'est, deux valeurs null sont égaux par définition.
  • La propriété HasValue est vrai, et la valeur retournée par la propriété Value est égale à l'autre paramètre.

et retourne false si:

  • La propriété HasValue pour l'actuelle structure Nullable est vrai, et l'autre paramètre est null.
  • La propriété HasValue pour l'actuelle structure Nullable est faux, et l'autre paramètre n'est pas nulle.
  • La propriété HasValue pour l'actuelle structure Nullable est vrai, et la valeur retournée par la Valeur de la propriété n'est pas l'égal de l'autre paramètre.

Plus d'info ici Nullable<.T>.Méthode Equals

5voto

Eric Petroelje Points 40734

Je suppose que c'est dû plutôt à un attribut commun des SGBD - Juste parce que deux choses sont à la fois nulle ne signifie pas qu'ils sont égaux.

Pour développer un peu, essayez d'exécuter ces deux requêtes:

SELECT * FROM TABLE WHERE field = NULL

SELECT * FROM TABLE WHERE field IS NULL

La raison de l' "EST NULL" construire, c'est que dans le SGBD monde, NULL != NUL depuis la signification de la valeur NULL est que la valeur est undefined. Depuis NULL signifie pas défini, vous ne pouvez pas dire que les deux valeurs null sont égaux, puisque, par définition, vous ne savez pas ce qu'ils sont.

Lorsque vous avez explicitement case à cocher pour "champ == NULL", LINQ probablement la convertit en "champ EST NULL". Mais lorsque vous utilisez une variable, je devine que LINQ ne le fait pas automatiquement que la conversion.

Voici un forum MSDN post avec plus d'info sur ce problème.

Ressemble à un bon "triche" est de changer votre lambda pour ressembler à ceci:

c => c.ParentId.Equals(categoryId)

1voto

Ryan Versaw Points 3682

Qu'en est-il quelque chose de plus simple comme ça?

 public IEnumerable<ICategory> GetSubCategories(long? categoryId)
{
    var subCategories = this.Repository.Categories.Where(c => (!categoryId.HasValue && c.ParentId == null) || c.ParentId == categoryId)
        .ToList().Cast<ICategory>();

    return subCategories;
}
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X