Il cast al tipo di valore 'Int32' non è riuscito perché il valore materializzato è nullo


193

Ho il codice seguente. Ricevo un errore:

"Il cast per il tipo di valore 'Int32' non è riuscito perché il valore materializzato è nullo. Il parametro generico del tipo di risultato o la query devono utilizzare un tipo nullable."

quando la tabella CreditHistory non ha record.

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();

Come posso modificare la query per accettare valori null?

Risposte:


330

Una query da linq a sql non viene eseguita come codice, ma piuttosto tradotta in SQL. A volte questa è una "astrazione che perde" che produce un comportamento inaspettato.

Uno di questi casi è la gestione null, dove possono esserci null imprevisti in luoghi diversi. ...DefaultIfEmpty(0).Sum(0)può aiutare in questo caso (abbastanza semplice), in cui potrebbero non esserci elementi e SUMrendimenti di sql nullmentre c # prevede 0.

Un approccio più generale è l'uso ??che verrà tradotto COALESCEogni volta che esiste il rischio che l'SQL generato restituisca un valore nullo imprevisto:

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;

Questo primo int?a dire al compilatore C # che questa espressione può effettivamente restituire null, anche se Sum()restituisce un int. Quindi utilizziamo il normale ??operatore per gestire il nullcaso.

Sulla base di questa risposta, ho scritto un post sul blog con i dettagli sia per LINQ to SQL sia per LINQ to Entities.


3
grazie Anders, la soluzione con DefaultIfEmpty (0) .Sum () funziona bene per me. Ho anche provato la seconda soluzione con (int?) ... ?? 0 ..., ma genera la stessa eccezione di prima ...
zosim,

Finalmente sono riuscito a provare questo e ad adattarlo, quindi ora funziona anche la seconda versione.
Anders Abel,

1
Sum () e altre funzioni di aggregazione restituiranno null quando vengono applicate a un set di dati vuoto. Contrariamente alla loro definizione, in realtà restituiscono una versione nullable del tipo sottostante.
Suncat2000,

2
@recursive: il tuo esempio è LINQ-to-Objects, non LINQ-to-SQL (o LINQ-to-Entities). I loro fornitori di dati sottostanti li fanno comportare diversamente.
Suncat2000,

Questa è stata una buona idea Ho aggiornato il mio oggetto di ritorno per avere proprietà nullable e che ha funzionato come un fascino.
Kremena Lalova,

8

Per consentire un Amountcampo nullable , basta usare l'operatore di coalescenza null per convertire i null in 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
quando uso il tuo suggerimento, il compilatore dice: Operatore '??' non può essere applicato agli operandi di tipo 'int' e 'int'. ho dimenticato qualcosa?
zosim,

@zosim: questa è la ragione per aggiungere prima il cast int?.
Anders Abel,

ho aggiunto int ?, ma la stessa eccezione. Ti sarò grato, quando avrai dev env. per verificare cosa non va in questa sintassi.
zosim,

1
@zosim: non capisco il problema. Se Amountè un int, allora siamo già sicuri che non può essere nullo e la coalescenza non è necessaria. Se ricevi l'errore che hai detto, allora Amountnon è nulla, è solo un int, nel qual caso forse devi cambiare la tua colonna dbml linq2sql nella finestra di progettazione per consentire null.
ricorsivo il

1
@recursive: l'importo è int, è OK. L'importo ha già valore. Penso che l'errore sopra si sia verificato perché la tabella CreditHistory è vuota. Ho un record nella tabella User e 0 record nella tabella CreditHistory e si è verificato un errore. Quando uso DefaultIfEmpty (0) .Sum () funziona benissimo, ma con ?? 0 genera errore. Un'altra mia domanda è qual è la migliore pratica in questo caso? DefaultIfEmpty (0)? grazie
zosim,

4

Si sta utilizzando la aggregatefunzione che non consente agli elementi di eseguire l'azione, è necessario verificare che la query linq stia dando alcuni risultati come di seguito:

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

11
Ciò renderebbe sdv eseguito due volte. Che non è quello che vuoi per IQueryables
Ody,

4

Ho avuto questo messaggio di errore quando stavo provando a selezionare da una vista.

Il problema era che la vista aveva recentemente acquisito alcune nuove righe null (nella colonna SubscriberId) e non era stata aggiornata in EDMX (prima il database EF).

La colonna doveva essere di tipo Nullable per funzionare.

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

Prima di aggiornare la vista:

public int SubscriberId { get; set; }

Dopo l'aggiornamento della vista:

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

L'eliminazione e l'aggiunta della vista in EDMX ha funzionato.

Spero che aiuti qualcuno.


Questo era anche il mio problema e la mia risposta
Simon Nicholls,

4

Ho usato questo codice e risponde correttamente, solo il valore di output è 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
                            }

1

Vedo che questa domanda ha già una risposta. Ma se vuoi che sia diviso in due affermazioni, puoi considerare quanto segue.

var credits = from u in context.User
              join ch in context.CreditHistory 
                  on u.ID equals ch.UserID                                        
              where u.ID == userID
              select ch;

var creditSum= credits.Sum(x => (int?)x.Amount) ?? 0;

0

Ottenuto questo errore in Entity Framework 6 con questo codice in fase di esecuzione:

var fileEventsSum = db.ImportInformations.Sum(x => x.FileEvents)

Aggiornamento da LeandroSoares:

Usa questo per singola esecuzione:

var fileEventsSum = db.ImportInformations.Sum(x => (int?)x.FileEvents) ?? 0

Originale:

Modificato in questo e poi ha funzionato:

var fileEventsSum = db.ImportInformations.Any() ? db.ImportInformations.Sum(x => x.FileEvents) : 0;

1
Non lo eseguiresti due volte?
nawfal,

Questa non è una buona risposta. Verrà recuperato dal DB due volte.
Leandro Soares,

@nawfal Questo è vero ma è molto meglio di un errore di runtime. Puoi assolutamente usare linq-to-sql ma con lambda è più difficile. Ovviamente puoi cogliere l'eccezione ma penso che la soluzione sia peggiore di due esecuzioni.
Ogglas,

@LeandroSoares vedi commento sopra
Ogglas

1
@LeandroSoares Nice one! Ho aggiornato la mia risposta e ho usato il codice che hai fornito e la descrizione del perché utilizzarlo.
Ogglas,

0

Stavo anche affrontando lo stesso problema e ho risolto rendendo nullable la colonna usando "?" operatore.

Sequnce = db.mstquestionbanks.Where(x => x.IsDeleted == false && x.OrignalFormID == OriginalFormIDint).Select(x=><b>(int?)x.Sequence</b>).Max().ToString();

A volte viene restituito null.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.