2012-06-29 9 views
6

Tengo una consulta LinqToEntities que produce una subconsulta al crear el SQL. Esto hace que el conjunto de resultados regrese con 0-3 resultados, cada vez que se ejecuta la consulta. La subconsulta en sí misma produce un único resultado aleatorio (como debería). ¿Que esta pasando aqui?LinqToEntities produce SQL incorrecto (subconsulta doble)

La consulta LINQ:

from jpj in JobProviderJobs 
where jpj.JobID == 4725 
&& jpj.JobProviderID == (from jp2 in JobProviderJobs 
         where jp2.JobID == 4725 
         orderby Guid.NewGuid() 
         select jp2.JobProviderID).FirstOrDefault() 
select new 
{ 
    JobProviderID = jpj.JobProviderID 
} 

producir este SQL:

SELECT 
[Filter2].[JobID] AS [JobID], 
[Filter2].[JobProviderID1] AS [JobProviderID] 
FROM (SELECT [Extent1].[JobID] AS [JobID], [Extent1].[JobProviderID] AS [JobProviderID1], [Limit1].[JobProviderID] AS [JobProviderID2] 
    FROM [dbo].[JobProviderJob] AS [Extent1] 
    LEFT OUTER JOIN (SELECT TOP (1) [Project1].[JobProviderID] AS [JobProviderID] 
     FROM (SELECT 
      NEWID() AS [C1], 
      [Extent2].[JobProviderID] AS [JobProviderID] 
      FROM [dbo].[JobProviderJob] AS [Extent2] 
      WHERE 4725 = [Extent2].[JobID] 
     ) AS [Project1] 
     ORDER BY [Project1].[C1] ASC) AS [Limit1] ON 1 = 1 
    WHERE 4725 = [Extent1].[JobID]) AS [Filter2] 
LEFT OUTER JOIN (SELECT TOP (1) [Project2].[JobProviderID] AS [JobProviderID] 
    FROM (SELECT 
     NEWID() AS [C1], 
     [Extent3].[JobProviderID] AS [JobProviderID] 
     FROM [dbo].[JobProviderJob] AS [Extent3] 
     WHERE 4725 = [Extent3].[JobID] 
    ) AS [Project2] 
    ORDER BY [Project2].[C1] ASC) AS [Limit2] ON 1 = 1 
WHERE [Filter2].[JobProviderID1] = (CASE WHEN ([Filter2].[JobProviderID2] IS NULL) THEN 0 ELSE [Limit2].[JobProviderID] END) 

EDIT:

Así que el cambio de la subconsulta para esto funciona, pero no tengo ni idea de por qué

(from jp2 in JobProviderJobs 
    where jp2.JobID == 4725 
    orderby Guid.NewGuid() 
    select jp2).FirstOrDefault().JobProviderID 

Respuesta

3

Está haciendo esto debido al comportamiento esperado de FirstOrDefault(). Llamar al FirstOrDefault() en un conjunto vacío de JobProviderJobs produciría un valor de null, pero invocarlo en un conjunto vacío de int generaría un 0. Reconociendo esto, LINQ to Entities intenta invocar una declaración de caso al final de la consulta para garantizar que si no hay JobProviderJobs coincidentes, el resultado de la selección será 0 en lugar de nulo.

En la mayoría de los casos, la recreación de la proyección interna no causaría problemas, pero el uso de NewGuid() obviamente descarta esta lógica.

Has encontrado una solución. Otro sería a emitir el resultado de la expresión interior de esta manera:

from jpj in JobProviderJobs 
where jpj.JobID == 4725 
&& jpj.JobProviderID == (from jp2 in JobProviderJobs 
         where jp2.JobID == 4725 
         orderby Guid.NewGuid() 
         select (int?) jp2.JobProviderID).FirstOrDefault() 
select new 
{ 
    JobProviderID = jpj.JobProviderID 
} 

Una implementación más correcta sería reutilizar la proyección inicial SQL en lugar de crear dos proyecciones equivalentes. Es probable que el analizador simplemente no sea lo suficientemente complejo como para manejarlo correctamente, y los desarrolladores nunca vieron la necesidad de solucionarlo porque SQL Server debería poder optimizar las proyecciones idénticas siempre que sean deterministas.

Probablemente deba registrar un informe de error acerca de esto si aún no existe uno.

+1

Ojalá pudiera más 3. Respuesta perfecta: Explicación del comportamiento, la solución y el uso de la palabra "thusly" =) – Tyrsius

+0

@Tyrsius: He tenido [éxito] (http://connect.microsoft.com/VisualStudio/feedback/details/658392/linq-to-entities-orderby-is-lost-when-followed-by-firstordefault) errores de registro en [Visual Studio y .NET] (https://connect.microsoft.com/VisualStudio/ Realimentación) – StriplingWarrior

Cuestiones relacionadas