web-dev-qa-db-fra.com

Requête Linq avec somme nulle

from i in Db.Items
select new VotedItem
{
    ItemId = i.ItemId,
    Points = (from v in Db.Votes
              where b.ItemId == v.ItemId
              select v.Points).Sum()
}

J'ai eu cette requête, mais elle échoue si aucun vote n'a été trouvé avec une exception:

The null value cannot be assigned to a member with type System.Int32 which is a non-nullable value type.

Je suppose que c'est parce que sum renvoie un int et non un int nullable, donnant à sum un int? en tant qu'entrée, donnez la même erreur, probablement parce que sum ne fonctionne que sur les ints.

Une bonne solution pour cela?

67
AndreasN
from i in Db.Items
select new VotedItem
{
    ItemId = i.ItemId,
    Points = (from v in Db.Votes
              where b.ItemId == v.ItemId
              select v.Points ?? 0).Sum() 
}

EDIT - ok, et alors ...

from i in Db.Items
select new VotedItem
{
    ItemId = i.ItemId,
    Points = (from v in Db.Votes
              where b.ItemId == v.ItemId)
              .Sum(v => v.Points) 
}
22
Rashack

Vous voulez utiliser la forme sumable de Sum, essayez donc de convertir votre valeur en une valeur nullable:

from i in Db.Items
select new VotedItem
{
    ItemId = i.ItemId,
    Points = (from v in Db.Votes
              where b.ItemId == v.ItemId
              select v.Points).Sum(r => (decimal?) r.Points)
}

Votre problème est discuté ici plus en détail: 

http://weblogs.asp.net/zeeshanhirani/archive/2008/07/15/applying-aggregates-to-empty-collections-causes-exception-in-linq-to-sql.aspx

73
Scott Stafford

En supposant que "v.Points" soit un nombre décimal, utilisez simplement les éléments suivants:

from i in Db.Items
select new VotedItem
{
    ItemId = i.ItemId,
    Points = (from v in Db.Votes
              where b.ItemId == v.ItemId
              select (decimal?) v.Points).Sum() ?? 0
}
19
Jeroen Bernsen

Si vous n'aimez pas la conversion en décimale nullabe, vous pouvez également utiliser la méthode Linq To Objects avec la méthode ToList ().

LinqToObjects La somme de la collection vide est 0, la somme LinqToSql de la collection vide est null.

11
Emir

Essayez de vérifier ceci:

var count = db.Cart.Where(c => c.UserName == "Name").Sum(c => (int?)c.Count) ?? 0;

Ainsi, la racine du problème est que la requête SQL ressemble à ceci:

SELECT SUM([Votes].[Value])
FROM [dbo].[Votes] AS [Votes]
WHERE 1 = [Votes].[UserId] 

renvoie NULL

10
Pavel Shkleinik

Une solution de contournement simple mais efficace consisterait à ne faire la somme que des votes où Points.Count> 0, de sorte que vous n'ayez jamais de valeurs nulles:

from i in Db.Items
select new VotedItem
{    
  ItemId = i.ItemId,
  Points = (from v in Db.Votes
            where b.ItemId == v.ItemId &&
            v.Points.Count > 0
            select v.Points).Sum()
}
5
Razzie

Juste pour ajouter une autre méthode dans le mélange :)

Where(q=> q.ItemId == b.ItemId && b.Points.HasValue).Sum(q=>q.Points.Value)

J'avais un scénario similaire mais je ne comparais pas un champ supplémentaire lors de la somme ...

Where(q => q.FinalValue.HasValue).Sum(q=>q.FinalValue.Value);
4
Phil

Assuming Points est une liste de Int32, que diriez-vous de quelque chose comme:

var sum = Points.DefaultIfEmpty().Sum(c => (Int32)c ?? 0)
3
ProfNimrod

Je pense que c'est le même cas ... Je l'ai résolu. Ceci est ma solution:

var x = (from a in this.db.Pohybs
                 let sum = (from p in a.Pohybs
                            where p.PohybTyp.Vydej == true
                            select p.PocetJednotek).Sum()
                 where a.IDDil == IDDil && a.PohybTyp.Vydej == false
                 && ( ((int?)sum??0) < a.PocetJednotek)
                 select a);

J'espère que cette aide.

2
Pepa

J'ai eu le même problème. Résolu avec l'union de liste vide:

List<int> emptyPoints = new List<int>() { 0 };

from i in Db.Items
select new VotedItem
{
 ItemId = i.ItemId,
 Points = (from v in Db.Votes
           where b.ItemId == v.ItemId
           select v.Points).Union(emptyPoints).Sum()
}

Dans le cas où "Points" est un entier, cela devrait fonctionner.

2
Alex

Peut-être mettre cette requête dans try/catch..if "exception", alors il n'y avait pas de votes

1
0x49D1

Semblable aux réponses précédentes, mais vous pouvez également convertir le résultat de la somme entière en type nullable.

from i in Db.Items
select new VotedItem
{
    ItemId = i.ItemId,
    Points = (decimal?)((from v in Db.Votes
              where b.ItemId == v.ItemId
              select v.Points).Sum()) ?? 0
}

On peut dire que cela correspond mieux à ce qui se passe réellement mais il a le même effet que le casting dans cette réponse .

0
OlduwanSteve
        (from i in Db.Items
         where (from v in Db.Votes
                where i.ItemId == v.ItemId
                select v.Points).Count() > 0
         select new VotedItem
         {
             ItemId = i.ItemId,
             Points = (from v in Db.Items
                       where i.ItemId == v.ItemId
                       select v.Points).Sum()
         }).Union(from i in Db.Items
                  where (from v in Db.Votes
                         where i.ItemId == v.ItemId
                         select v.Points).Count() == 0
                  select new VotedItem
                  {
                      ItemId = i.ItemId,
                      Points = 0
                  }).OrderBy(i => i.Points);

Cela fonctionne, mais n'est pas très joli ou lisible.

0
AndreasN

Je pensais lancer une autre solution. J'ai eu un problème similaire et voici comment j'ai fini par le résoudre:

Where(a => a.ItemId == b.ItemId && !b.IsPointsNull()).Sum(b => b.Points)
0
Jeff

J'avais un problème similaire et j'ai trouvé la solution qui consiste à récupérer tout ce que j'essayais de récupérer dans la base de données, à compter dessus et à ne payer que la somme restante. N'a pas pu faire fonctionner le casting pour une raison quelconque, donc l'affiche si quelqu'un d'autre avait des problèmes similaires.

par exemple.

Votes = (from v in Db.Votes
          where b.ItemId = v.ItemId
          select v)

Et ensuite, vérifiez si vous avez des résultats afin de ne pas obtenir la valeur null.

If (Votes.Count > 0) Then
    Points = Votes.Sum(Function(v) v.Points)
End If
0
tcmorris