LINQ TO SQL: Uso de NULL
Leyendo C# 3.0 in a Nutshell (Pedazo de libro) he podido aprender acerca del uso de null en consultas LINQ TO SQL y voy a intentar explicarlo como yo lo he entendido.
Partiendo de esta tabla de ejemplo:
Vamos a empezar a lanzar consultas utilizando LINQPad y a observar los resultados:
Customers.Where(c => c.Telephone == null).Dump("IS NULL");

La sentencia SQL que se genera:
SELECT [t0].[CustomerId], [t0].[Name], [t0].[Surname], [t0].[Telephone], [t0].[Email]
FROM [Customers] AS [t0]
WHERE [t0].[Telephone] IS NULL
Como vemos, la instrucción c.Telephone == null, LINQ to SQL la está traduciendo a IS NULL, que como sabemos es la manera de consultar por valores nulos en TSQL.
¿Que pasa sí en vez de utilizar el valor estático null usamos una variable de tipo Nullable<T>?
int? telephone = null;
Customers.Where(c => c.Telephone == telephone).Dump(" = @P");
Pues que no retorna nada y el porque se puede apreciar en la consulta SQL que LINQ to SQL emite:
SELECT [t0].[CustomerId], [t0].[Name], [t0].[Surname], [t0].[Telephone], [t0].[Email]
FROM [Customers] AS [t0]
WHERE [t0].[Telephone] = @p0
-- @p0: Input Int (Size = 0; Prec = 0; Scale = 0) [Null]
Está claro no? No podemos hacer comparaciones en TSQL del Tipo Campo = NULL
¿Cómo se puede solucionar esto?
Usando object.Equals, que sí el valor de la variable es NULL, LINQ generará un IS NULL y sí tiene valor generará = @p
Customers.Where(c => object.Equals(c.Telephone, telephone)).Dump("object.Equals nullable");

SELECT [t0].[CustomerId], [t0].[Name], [t0].[Surname], [t0].[Telephone], [t0].[Email]
FROM [Customers] AS [t0]
WHERE [t0].[Telephone] IS NULL
Y sí la variable tiene valor:
telephone = 999999999;
Customers.Where(c => object.Equals(c.Telephone, telephone)).Dump("object.Equals non nullable");

SELECT [t0].[CustomerId], [t0].[Name], [t0].[Surname], [t0].[Telephone], [t0].[Email]
FROM [Customers] AS [t0]
WHERE ([t0].[Telephone] IS NOT NULL) AND ([t0].[Telephone] = @p0)
-- @p0: Input Int (Size = 0; Prec = 0; Scale = 0) [999999999]
Stored Procedures:
Una cosa que hago desde hace tiempo en mi SPs es utilizar sentencias del tipo:
(ISNULL(Name,0) LIKE ISNULL(@Name,ISNULL(Name,0)))
para crear consultas dinámicas y he estado intentando hacerlo desde LINQ to SQL pero no lo he conseguido y no sé sí se podrá claro :)
Así que he optado por crear el SPs tal cual el ejemplo:
CREATE PROCEDURE [dbo].[sp_GetCustomers]
@Name nvarchar(50),
@Surname nvarchar(50),
@Telephone int,
@Email nvarchar(100)
AS
BEGIN
SET NOCOUNT ON;
SELECT Name,
Surname,
Telephone,
Email
FROM Customers
WHERE (ISNULL(Name,0) LIKE ISNULL(@Name,ISNULL(Name,0)))
AND (ISNULL(Surname,0) LIKE ISNULL(@Surname,ISNULL(Surname,0)))
AND (ISNULL(Telephone,0) = ISNULL(@Telephone,ISNULL(Telephone,0)))
AND (ISNULL(Email,0) = ISNULL(@Email,ISNULL(Email,0)))
END
Y llamarlo directamente:
var customers1 = sp_GetCustomers(null,null,null,null);
customers1.Dump("SP ALL NULL");
var customers2 = sp_GetCustomers("michael",null,null,null);
customers2.Dump("SP Name");
Pues esto es todo!!!