¿Cómo puedo consultar valores nulos en el marco de la entidad?

Quiero ejecutar una consulta como esta

var result = from entry in table where entry.something == null select entry; 

y obtener un IS NULL generado.

Editado: después de las dos primeras respuestas, siento la necesidad de aclarar que estoy usando Entity Framework y no Linq to SQL. El método object.Equals () no parece funcionar en EF.

Edit no.2: la consulta anterior funciona como se esperaba. Genera correctamente IS NULL . Sin embargo, mi código de producción era

 value = null; var result = from entry in table where entry.something == value select entry; 

y el SQL generado fue something = @p; @p = NULL something = @p; @p = NULL . Parece que EF traduce correctamente la expresión constante, pero si una variable está involucrada, la trata como una comparación normal. Tiene sentido en realidad. Cerraré esta pregunta

Solución para Linq-to-SQL:

 var result = from entry in table where entry.something.Equals(value) select entry; 

Solución para Linq-to-Entities (¡ay!):

 var result = from entry in table where (value == null ? entry.something == null : entry.something == value) select entry; 

Este es un error desagradable que me ha mordido varias veces. Si este error también te ha afectado, visita el informe de errores en UserVoice y hazle saber a Microsoft que este error también te ha afectado.


Editar: ¡ Este error se soluciona en EF 4.5 ! ¡Gracias a todos por votar este error!

Para compatibilidad con versiones anteriores, será opt-in; debe habilitar manualmente una configuración para que la entry == value funcione. No se sabe aún qué es esta configuración. ¡Manténganse al tanto!


Edición 2: según esta publicación del equipo de EF, este problema se ha solucionado en EF6. Woohoo!

Cambiamos el comportamiento predeterminado de EF6 para compensar la lógica de tres valores.

Esto significa que el código existente que se basa en el comportamiento antiguo ( null != null , pero solo cuando se compara con una variable) deberá cambiarse para no confiar en ese comportamiento o establecer UseCSharpNullComparisonBehavior en falso para usar el antiguo comportamiento UseCSharpNullComparisonBehavior .

Desde Entity Framework 5.0 puede usar el siguiente código para resolver su problema:

 public abstract class YourContext : DbContext { public YourContext() { (this as IObjectContextAdapter).ObjectContext.ContextOptions.UseCSharpNullComparisonBehavior = true; } } 

Esto debería resolver sus problemas ya que Entity Framerwork usará una comparación nula ‘C # like’.

Hay una solución un poco más simple que funciona con LINQ to Entities:

 var result = from entry in table where entry.something == value || (value == null && entry.something == null) select entry; 

Esto funciona porque, como notó AZ, LINQ to Entities casos especiales x == null (es decir, una comparación de igualdad contra la constante nula) y lo traduce en x IS NULL.

Actualmente estamos considerando cambiar este comportamiento para presentar las comparaciones de compensación automáticamente si ambos lados de la igualdad son nulables. Sin embargo, hay un par de desafíos:

  1. Esto podría potencialmente romper el código que ya depende del comportamiento existente.
  2. La nueva traducción podría afectar el rendimiento de las consultas existentes, incluso cuando un parámetro nulo rara vez se utiliza.

En cualquier caso, si trabajamos en esto, dependerá en gran medida de la prioridad relativa que nuestros clientes le asignen. Si le importa el problema, lo aliento a que lo vote en nuestro nuevo sitio Sugerencia de funciones: https://data.uservoice.com .

Si es un tipo que admite nulos, tal vez intente usar la propiedad HasValue?

 var result = from entry in table where !entry.something.HasValue select entry; 

No tengo ningún EF para probar aquí … solo una sugerencia =)

 var result = from entry in table where entry.something.Equals(null) select entry; 

Referencia de MSDN : LINQ to SQL: consulta integrada de lenguaje .NET para datos relacionales

para tratar con Comparaciones nulas use Object.Equals() lugar de ==

revisa esta referencia

 var result = from entry in table where entry.something == null select entry; 

La consulta anterior funciona según lo previsto. Genera correctamente IS NULL. Sin embargo, mi código de producción era

 var value = null; var result = from entry in table where entry.something == value select entry; 

y el SQL generado fue algo = @p; @p = NULL. Parece que EF traduce correctamente la expresión constante, pero si una variable está involucrada, la trata como una comparación normal. Tiene sentido en realidad.

Señalando que todas las sugerencias de Entity Framework <6.0 generan algunos SQL incómodos. Vea el segundo ejemplo de solución "limpia".

Solución ridícula

 // comparing against this... Foo item = ... return DataModel.Foos.FirstOrDefault(o => o.ProductID == item.ProductID // ridiculous < EF 4.5 nullable comparison workaround http://stackoverflow.com/a/2541042/1037948 && item.ProductStyleID.HasValue ? o.ProductStyleID == item.ProductStyleID : o.ProductStyleID == null && item.MountingID.HasValue ? o.MountingID == item.MountingID : o.MountingID == null && item.FrameID.HasValue ? o.FrameID == item.FrameID : o.FrameID == null && o.Width == w && o.Height == h ); 

resultados en SQL como:

 SELECT TOP (1) [Extent1].[ID] AS [ID], [Extent1].[Name] AS [Name], [Extent1].[DisplayName] AS [DisplayName], [Extent1].[ProductID] AS [ProductID], [Extent1].[ProductStyleID] AS [ProductStyleID], [Extent1].[MountingID] AS [MountingID], [Extent1].[Width] AS [Width], [Extent1].[Height] AS [Height], [Extent1].[FrameID] AS [FrameID], FROM [dbo].[Foos] AS [Extent1] WHERE (CASE WHEN (([Extent1].[ProductID] = 1 /* @p__linq__0 */) AND (NULL /* @p__linq__1 */ IS NOT NULL)) THEN CASE WHEN ([Extent1].[ProductStyleID] = NULL /* @p__linq__2 */) THEN cast(1 as bit) WHEN ([Extent1].[ProductStyleID] <> NULL /* @p__linq__2 */) THEN cast(0 as bit) END WHEN (([Extent1].[ProductStyleID] IS NULL) AND (2 /* @p__linq__3 */ IS NOT NULL)) THEN CASE WHEN ([Extent1].[MountingID] = 2 /* @p__linq__4 */) THEN cast(1 as bit) WHEN ([Extent1].[MountingID] <> 2 /* @p__linq__4 */) THEN cast(0 as bit) END WHEN (([Extent1].[MountingID] IS NULL) AND (NULL /* @p__linq__5 */ IS NOT NULL)) THEN CASE WHEN ([Extent1].[FrameID] = NULL /* @p__linq__6 */) THEN cast(1 as bit) WHEN ([Extent1].[FrameID] <> NULL /* @p__linq__6 */) THEN cast(0 as bit) END WHEN (([Extent1].[FrameID] IS NULL) AND ([Extent1].[Width] = 20 /* @p__linq__7 */) AND ([Extent1].[Height] = 16 /* @p__linq__8 */)) THEN cast(1 as bit) WHEN (NOT (([Extent1].[FrameID] IS NULL) AND ([Extent1].[Width] = 20 /* @p__linq__7 */) AND ([Extent1].[Height] = 16 /* @p__linq__8 */))) THEN cast(0 as bit) END) = 1 

Escalonada solución

Si desea generar SQL más limpio, algo como:

 // outrageous < EF 4.5 nullable comparison workaround http://stackoverflow.com/a/2541042/1037948 Expression> filterProductStyle, filterMounting, filterFrame; if(item.ProductStyleID.HasValue) filterProductStyle = o => o.ProductStyleID == item.ProductStyleID; else filterProductStyle = o => o.ProductStyleID == null; if (item.MountingID.HasValue) filterMounting = o => o.MountingID == item.MountingID; else filterMounting = o => o.MountingID == null; if (item.FrameID.HasValue) filterFrame = o => o.FrameID == item.FrameID; else filterFrame = o => o.FrameID == null; return DataModel.Foos.Where(o => o.ProductID == item.ProductID && o.Width == w && o.Height == h ) // continue the outrageous workaround for proper sql .Where(filterProductStyle) .Where(filterMounting) .Where(filterFrame) .FirstOrDefault() ; 

resultados en lo que quería en primer lugar:

 SELECT TOP (1) [Extent1].[ID] AS [ID], [Extent1].[Name] AS [Name], [Extent1].[DisplayName] AS [DisplayName], [Extent1].[ProductID] AS [ProductID], [Extent1].[ProductStyleID] AS [ProductStyleID], [Extent1].[MountingID] AS [MountingID], [Extent1].[Width] AS [Width], [Extent1].[Height] AS [Height], [Extent1].[FrameID] AS [FrameID], FROM [dbo].[Foos] AS [Extent1] WHERE ([Extent1].[ProductID] = 1 /* @p__linq__0 */) AND ([Extent1].[Width] = 16 /* @p__linq__1 */) AND ([Extent1].[Height] = 20 /* @p__linq__2 */) AND ([Extent1].[ProductStyleID] IS NULL) AND ([Extent1].[MountingID] = 2 /* @p__linq__3 */) AND ([Extent1].[FrameID] IS NULL) 

Parece que Linq2Sql también tiene este “problema”. Parece que hay una razón válida para este comportamiento debido a si ANSI NULLs están activados o desactivados, pero no deja de entender por qué un “== nulo” directo funcionará como es de esperar.

Personalmente, prefiero:

 var result = from entry in table where (entry.something??0)==(value??0) select entry; 

encima

 var result = from entry in table where (value == null ? entry.something == null : entry.something == value) select entry; 

porque evita la repetición, aunque eso no es matemáticamente exacto, pero se adapta bien a la mayoría de los casos.

No puedo comentar la publicación de divega, pero entre las diferentes soluciones presentadas aquí, la solución de divega produce el mejor SQL. Tanto en rendimiento como en longitud. Acabo de verificar con SQL Server Profiler y mirando el plan de ejecución (con “SET STATISTICS PROFILE ON”).

Desafortunadamente, en Entity Framework 5 DbContext, el problema aún no está solucionado.

Usé esta solución alternativa (funciona con MSSQL 2012 pero la configuración ANSI NULLS podría estar obsoleta en cualquier versión futura de MSSQL).

 public class Context : DbContext { public Context() : base("name=Context") { this.Database.Connection.StateChange += Connection_StateChange; } void Connection_StateChange(object sender, System.Data.StateChangeEventArgs e) { // Set ANSI_NULLS OFF when any connection is opened. This is needed because of a bug in Entity Framework // that is not fixed in EF 5 when using DbContext. if (e.CurrentState == System.Data.ConnectionState.Open) { var connection = (System.Data.Common.DbConnection)sender; using (var cmd = connection.CreateCommand()) { cmd.CommandText = "SET ANSI_NULLS OFF"; cmd.ExecuteNonQuery(); } } } } 

Cabe señalar que es una solución sucia, pero es una que se puede implementar muy rápidamente y funciona para todas las consultas.

Si prefiere usar la syntax de método (lambda) como lo hago, podría hacer lo mismo así:

 var result = new TableName(); using(var db = new EFObjectContext) { var query = db.TableName; query = value1 == null ? query.Where(tbl => tbl.entry1 == null) : query.Where(tbl => tbl.entry1 == value1); query = value2 == null ? query.Where(tbl => tbl.entry2 == null) : query.Where(tbl => tbl.entry2 == value2); result = query .Select(tbl => tbl) .FirstOrDefault(); // Inspect the value of the trace variable below to see the sql generated by EF var trace = ((ObjectQuery) query).ToTraceString(); } return result; 
 var result = from entry in table where entry.something == value||entry.something == null select entry; 

usa eso