210 votes

Le transfert vers le type de valeur 'Int32' a échoué car la valeur matérialisée est nulle.

J'ai le code suivant. J'obtiens une erreur :

"Le cast vers le type de valeur 'Int32' a échoué car la valeur matérialisée est nulle. Soit le paramètre générique du type de résultat, soit la requête doit utiliser un type nullable."

lorsque la table CreditHistory ne contient aucun enregistrement.

var creditsSum = (from u in context.User
                  join ch in context.CreditHistory on u.ID equals ch.UserID                                        
                  where u.ID == userID
                  select ch.Amount).Sum();

Comment puis-je modifier la requête pour qu'elle accepte les valeurs nulles ?

361voto

Anders Abel Points 36203

Une requête linq-to-sql n'est pas exécutée en tant que code, mais plutôt traduite en SQL. Il s'agit parfois d'une "abstraction fuyante" qui produit un comportement inattendu.

L'un de ces cas est la gestion des nullités, où il peut y avoir des nullités inattendues à différents endroits. ...DefaultIfEmpty(0).Sum(0) peut aider dans ce cas (assez simple), où il n'y a peut-être pas d'éléments et où la fonction sql SUM renvoie à null alors que le c# attend 0.

Une approche plus générale consiste à utiliser ?? qui sera traduit en COALESCE lorsqu'il y a un risque que le SQL généré renvoie un null inattendu :

var creditsSum = (from u in context.User
              join ch in context.CreditHistory on u.ID equals ch.UserID                                        
              where u.ID == userID
              select (int?)ch.Amount).Sum() ?? 0;

Cette première jette à int? pour dire au compilateur C# que cette expression peut effectivement retourner null même si Sum() renvoie un int . Ensuite, nous utilisons la méthode normale ?? pour traiter le null cas.

Sur la base de cette réponse, j'ai écrit un article de blog avec des détails pour LINQ to SQL et LINQ to Entities.

3 votes

Merci Anders, la solution avec DefaultIfEmpty(0).Sum() fonctionne bien pour moi. J'ai également essayé la deuxième solution avec (int ?) ... ? ? 0..., mais elle jette la même exception que précédemment...

0 votes

J'ai finalement testé et ajusté cette version, et maintenant la deuxième version fonctionne aussi.

1 votes

Sum() et les autres fonctions d'agrégation renvoient un résultat nul lorsqu'elles sont appliquées à un ensemble de données vide. Contrairement à leur définition, elles renvoient en réalité une version nullable du type sous-jacent.

8voto

recursive Points 34729

Pour permettre un Amount il suffit d'utiliser l'opérateur de coalescence des nuls pour convertir les nuls en 0.

var creditsSum = (from u in context.User
              join ch in context.CreditHistory on u.ID equals ch.UserID                                        
              where u.ID == userID
              select ch.Amount ?? 0).Sum();

1 votes

Quand j'utilise votre conseil, le compilateur dit : L'opérateur '??' ne peut pas être appliqué aux opérandes de type 'int' et 'int'. ai-je oublié quelque chose ?

0 votes

@zosim : C'est la raison pour laquelle il faut ajouter le casting à int? d'abord.

0 votes

J'ai ajouté int ?, mais la même exception. Je vous serai reconnaissant, lorsque vous aurez l'environnement de développement pour vérifier ce qui ne va pas dans cette syntaxe.

6voto

MohammadSoori Points 466

J'ai utilisé ce code et il répond correctement, seule la valeur de sortie est nullable.

var packesCount = await botContext.Sales.Where(s => s.CustomerId == cust.CustomerId && s.Validated)
                                .SumAsync(s => (int?)s.PackesCount);
                            if(packesCount != null)
                            {
                                // your code
                            }
                            else
                            {
                                // your code
                            }

6voto

40-Love Points 1333

J'ai reçu ce message d'erreur lorsque j'ai essayé de faire une sélection dans une vue.

Le problème était que la vue avait récemment gagné quelques nouvelles lignes nulles (dans la colonne SubscriberId), et qu'elle n'avait pas été mise à jour dans EDMX (base de données EF d'abord).

La colonne devait être de type Nullable pour que cela fonctionne.

var dealer = Context.Dealers.Where(x => x.dealerCode == dealerCode).FirstOrDefault() ;

Avant le rafraîchissement de la vue :

public int SubscriberId { get; set; }

Après le rafraîchissement de la vue :

public Nullable<int> SubscriberId { get; set; }

La suppression et la réintégration de la vue dans EDMX ont fonctionné.

J'espère que cela aidera quelqu'un.

5voto

Ashwini Points 17

Vous utilisez aggregate qui n'obtient pas les éléments pour effectuer l'action, vous devez vérifier que la requête linq donne un résultat comme ci-dessous :

var maxOrderLevel =sdv.Any()? sdv.Max(s => s.nOrderLevel):0

11 votes

Cela ferait en sorte que sdv s'exécute deux fois. Ce qui n'est pas ce que vous voulez pour les IQueryables.

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