web-dev-qa-db-fra.com

Max valeur de retour si requête vide

J'ai cette requête:

int maxShoeSize = Workers
    .Where(x => x.CompanyId == 8)
    .Max(x => x.ShoeSize);

Que sera-t-il dans maxShoeSize si la société 8 n’a aucun travailleur?

METTRE À JOUR:
Comment puis-je changer la requête afin d’obtenir 0 et pas une exception?

118
Naor
int maxShoeSize = Workers.Where(x => x.CompanyId == 8)
                         .Select(x => x.ShoeSize)
                         .DefaultIfEmpty(0)
                         .Max();

Le zéro dans DefaultIfEmpty n'est pas nécessaire.

223
Ron K.

Je sais que cette question est ancienne et que la réponse acceptée fonctionne, mais cette question répondait à ma question de savoir si un tel ensemble vide entraînerait une exception ou un résultat default(int).

La réponse acceptée cependant, bien que cela fonctionne, n’est pas la solution idéale IMHO, qui n’est pas donnée ici. Ainsi, je le fournis dans ma propre réponse au profit de tous ceux qui le recherchent.

Le code d'origine du PO était le suivant:

int maxShoeSize = Workers.Where(x => x.CompanyId == 8).Max(x => x.ShoeSize);

Voici comment je l'écrirais pour éviter les exceptions et fournir un résultat par défaut:

int maxShoeSize = Workers.Where(x => x.CompanyId == 8).Max(x => x.ShoeSize as int?) ?? 0;

Ainsi, le type de retour de la fonction Max est int?, ce qui permet d'obtenir le résultat null, puis le ?? remplace le résultat null par 0


MODIFIER
Juste pour clarifier quelque chose à partir des commentaires, Entity Framework ne prend pas en charge le mot clé as.

int maxShoeSize = Workers.Where(x => x.CompanyId == 8).Max<[TypeOfWorkers], int?>(x => x.ShoeSize) ?? 0;

Puisque le [TypeOfWorkers] pourrait être un nom de classe long et qu'il est fastidieux à écrire, j'ai ajouté une méthode d'extension pour vous aider.

public static int MaxOrDefault<T>(this IQueryable<T> source, Expression<Func<T, int?>> selector, int nullValue = 0)
{
    return source.Max(selector) ?? nullValue;
}

Ceci ne traite que int, mais la même chose peut être faite pour long, double ou tout autre type de valeur dont vous avez besoin. L'utilisation de cette méthode d'extension est très simple, il vous suffit de passer votre fonction de sélecteur et d'inclure éventuellement une valeur à utiliser pour null, dont la valeur par défaut est 0. Donc, la procédure ci-dessus pourrait être réécrite de la manière suivante:

int maxShoeSize = Workers.Where(x => x.CompanyId == 8).MaxOrDefault(x => x.ShoeSize);

Espérons que cela aide les gens encore plus.

45
CptRobby

Max () ne retournera rien dans ce cas.

Il déclenchera InvalidOperationException puisque la source ne contient aucun élément.

24
Frédéric Hamidi
int maxShoeSize = Workers.Where(x => x.CompanyId == 8)
                     .Select(x => x.ShoeSize)
                     .DefaultIfEmpty()
                     .Max();
16
Danny Chen

S'il s'agit de Linq to SQL, je n'aime pas utiliser Any() car il en résulte plusieurs requêtes sur le serveur SQL.

Si ShoeSize n'est pas un champ nullable, utiliser uniquement la fonction .Max(..) ?? 0 ne fonctionnera pas, mais les suivants:

int maxShoeSize = Workers.Where(x = >x.CompanyId == 8).Max(x => (int?)x.ShoeSize) ?? 0;

Cela ne change absolument pas le code SQL émis, mais renvoie 0 si la séquence est vide, car la fonction Max() est remplacée par un int? au lieu d'une int.

3
abkonsta
int maxShoeSize=Workers.Where(x=>x.CompanyId==8)
    .Max(x=>(int?)x.ShoeSize).GetValueOrDefault();

(en supposant que ShoeSize est de type int)

Si Workers est une DbSet ou ObjectSet de Entity Framework, votre requête initiale lancera une InvalidOperationException, mais ne se plaindra pas d'une séquence vide mais se plaindra que la valeur matérialisée NULL ne peut pas être convertie en int.

2
Slauma

NB: la requête avec DefaultIfEmpty() peut être significativement plus lente . Dans mon cas, il s’agissait d’une requête simple avec .DefaultIfEmpty(DateTime.Now.Date).

J'étais trop paresseux pour le profiler mais EF a évidemment essayé d'obtenir toutes les lignes, puis de prendre la valeur Max().

Conclusion: parfois manipuler InvalidOperationException pourrait être le meilleur choix.

2
Andrey St

Max lancera System.InvalidOperationException "La séquence ne contient aucun élément"

class Program
{
    static void Main(string[] args)
    {
        List<MyClass> list = new List<MyClass>();

        list.Add(new MyClass() { Value = 2 });

        IEnumerable<MyClass> iterator = list.Where(x => x.Value == 3); // empty iterator.

        int max = iterator.Max(x => x.Value); // throws System.InvalidOperationException
    }
}

class MyClass
{
    public int Value;
}
2
Johan Tidén

Vous pouvez essayer ceci:

int maxShoeSize = Workers.Where(x=>x.CompanyId == 8).Max(x => x.ShoeSize) ?? 0;
0
Carlos Toledo

Vous pouvez vérifier s'il y a des ouvriers avant de faire le Max ().

private int FindMaxShoeSize(IList<MyClass> workers) {
   var workersInCompany = workers.Where(x => x.CompanyId == 8);
   if(!workersInCompany.Any()) { return 0; }
   return workersInCompany.Max(x => x.ShoeSize);
}
0
Reverend Sfinks

Vous pouvez utiliser un élément ternaire dans .Max() pour gérer le prédicat et définir sa valeur;

// assumes Workers != null && Workers.Count() > 0
int maxShoeSize = Workers.Max(x => (x.CompanyId == 8) ? x.ShoeSize : 0);

Vous devez gérer la collection Workers comme nulle/vide si c'est une possibilité, mais cela dépend de votre implémentation.

0
Jecoms