2011-02-10 15 views
11

Ho una tabella chiamata "test", che ha solo 1 colonna, "NullableInt" (nullable int type)LINQ restituisce 0 se si usano nullable int, risultati accurati variabili se si utilizza "nullo"

I record sono : 1, 2, null

int? nullableInt = null; 
var t = db.tests.Where(x => x.NullableInt == null).ToList(); // returns 1 record 
var t2 = db.tests.Where(x => x.NullableInt == nullableInt).ToList(); // returns 0 records 

Per qualche ragione, t2 restituisce 0 record, anche tho sta usando "nullableInt" variabile, che ha un valore di null, proprio come t, che è il confronto contro il "null"

Qualsiasi aiuto sarebbe molto apprezzato!

risposta

2

query potrebbe essere costruito in questo modo:

var q = db.tests; 
if(nullableInt.HasValue) 
{ 
    q = q.Where(x => x.NullableInt == nullableInt.Value); 
} 
else 
{ 
    q = q.Where(x => x.NullableInt == null); 
} 
var t2 = q.ToList(); 
+0

+1 Fa schifo, ma è l'unico modo che sembra = ( – Francisco

+0

Vedere la mia risposta qui sotto, questa è fissata in EF6 e si può optare per una correzione in EF5. –

6

Sì, si tratta di un bug in LINQ-to-SQL/Entity Framework. Le query IS NULL verranno generate solo se si annulla il valore null nella query, invece di una variabile che al momento risulta null.

La seconda query genererà

SELECT ....... 
WHERE NullableInt == @someParam 
WHERE @someParam is null. 

dove la prima genererà l'appropriato IS NULL nella clausola WHERE.

Se si utilizza LINQ-to-SQL, è possibile registrare le query in Console.Out per vedere se stessi, e se si utilizza EF, quindi ToTraceString() dovrebbe mostrare le stesse informazioni (o SQL profilatore server)

+2

In realtà non penso che sia un bug perché nella seconda espressione 'nullableInt' non è' null', anche se assegni il suo valore con 'nullableInt = null' (dato che è una struct, il cui valore non può essere null) . Quindi il framework lo tratta come altre strutture (come un int). –

+2

@Danny, sta dividendo i capelli. Il bug potrebbe essere nella specifica, anche se il codice fa esattamente ciò che le specifiche dicono che dovrebbe fare. In altre parole, il bug qui potrebbe non essere che il codice capita di fare qualcosa di sbagliato, ma che qualcuno non ha considerato questo scenario. –

+1

Dividere i capelli davvero. Non c'è motivo per cui il parser EF debba generare la seconda query in modo che sia diverso da IS NULL. E questo è stato registrato come un bug con MS (con un voto molto alto) anche se non ho quel link a portata di mano. –

0

C'è un'altra soluzione che funziona sempre, anche se con un piccolo avvertimento:

int? nullableInt = null; 
var t2 = db.tests.Where(x => object.Equals(x.NullableInt, nullableInt)).ToList(); 

Quando il valore è nullo y ou otterrà la corretta IS NULL interrogazione, ma quando il suo non è nulla si otterrà qualcosa di simile a:

SELECT ... 
WHERE ([t0].[NullableInt] IS NOT NULL) AND ([t0].[NullableInt] = @p0) 

Ovviamente ha una condizione in più (la cui fonte è una specie di sconcertante). Detto questo, Query Optimizer di SQL Server dovrebbe rilevare che, poiché @ p0 è un valore non nullo, la prima condizione è un superset e taglierà la clausola where.

+0

Che può funzionare in LINQ-to-SQL (I haven lo ha testato) ma sicuramente * non * funziona in EF4, ma genera ancora lo stesso vecchio = @param dove @param è impostato su null –

+0

Ah, quindi lo hanno "riparato";). Sì, in L2S funziona. – mmix

0

Would fare:

var t2 = db.tests.Where(x => x.NullableInt == nullableInt ?? null).ToList(); 

lavoro?

Tuttavia sembra una pazzia totale.

+0

Follia davvero, e no, non funziona. Ho provato a inserire l'SQl generato, ma continua a non funzionare - suppongo che stia proteggendo da una sorta di attacco per iniezione –

2

tl; dr

Se si utilizza DbContext in EF6 questo è fisso.

Se si utilizza EF5 (o ObjectContext in EF6) è necessario impostare ObjectContext.ContextOptions.UseCSharpNullComparisonBehavior su true. Per farlo su DbContext usa questo:

((IObjectContextAdapter)db).ObjectContext.ContextOptions.UseCSharpNullComparisonBehavior = true; 

.

Maggiori dettagli

La causa principale di questo problema è la differenza nel modo in cui il database di confronto valori nulli e come C# confronta i valori nulli. Poiché scrivi la tua query in C#, vuoi usare la semantica di C#.

In EF5 abbiamo introdotto ObjectContext.ContextOptions.UseCSharpNullComparisonBehavior, che consente di scegliere di utilizzare la semantica C# invece della semantica del database. L'impostazione predefinita è false (in modo che le query esistenti non inizino magicamente a restituire risultati diversi quando si esegue l'aggiornamento a EF5). Ma puoi impostarlo su true e entrambe le tue query restituiranno righe.

Se si utilizza DbContext in EF5 è necessario scendere fino a ObjectContext per impostarlo:

((IObjectContextAdapter)db).ObjectContext.ContextOptions.UseCSharpNullComparisonBehavior = true; 

Se si utilizza EF6, allora è già impostata su true DbContext in modo che siano pronti per partire . Abbiamo deciso che questo causava così tanta confusione che valeva la pena prendere il potenziale impatto sulle query esistenti.

+0

Grazie Rowan, questo è stato molto utile. – Eric

Problemi correlati