none
No se ejecuta el SQL que esperaba en consulta LINQ RRS feed

  • Pregunta

  • Buenas,

    Tengo la siguiente consulta Linq

    var operacionesPermitidas = databaseContext.Set<ENRoleOperacion>();
    var recursos = databaseContext.Set<ENRecurso>();
    var operaciones = databaseContext.Set<ENOperacion>();
    
    var query = from opPer in operacionesPermitidas
    			join op in operaciones on new { opPer.ModuloId, opPer.RecursoId, opPer.OperacionId } equals new { op.ModuloId, op.RecursoId, op.OperacionId }
    			join rec in recursos on new { opPer.ModuloId, opPer.RecursoId } equals new { rec.ModuloId, rec.RecursoId }
    			where
    				rec.Clave == claveRecurso
    			select op;
    
    resultado = query.Any();

    Cuando examino la consulta generada a través del SQL-Profiler me muestra lo siguiente 

    exec sp_executesql N'SELECT 
        CASE WHEN ( EXISTS (SELECT 
            1 AS [C1]
            FROM  [Seguridad].[RoleOperacion] AS [Extent1]
            INNER JOIN [Seguridad].[Recurso] AS [Extent2] ON ([Extent1].[RecursoId] = [Extent2].[RecursoId]) AND ([Extent1].[ModuloId] = [Extent2].[ModuloId])
            WHERE [Extent2].[Clave] = @p__linq__0
        )) THEN cast(1 as bit) ELSE cast(0 as bit) END AS [C1]
        FROM  ( SELECT 1 AS X ) AS [SingleRowTable1]',N'@p__linq__0 varchar(8000)',@p__linq__0='GM-Admin'

    Veo que sólo genera un JOIN con la tabla Recurso  ¿qué pasó con el otro join de la tabla Operación?


    • Editado eduar2083 lunes, 26 de agosto de 2019 15:13
    lunes, 26 de agosto de 2019 15:13

Todas las respuestas

  • Dado que nunca llegas a ejecutar la query en sí misma, sino que solo ejecutas query.Any(), el LINQ solo tiene que devolver un booleano diciendo si existe o no existe un registro que cumpla la join. Y puede ser que no necesite hacer intervenir la tabla operaciones para saber si el join existe. Esto ocurriría si la condición de unión con operacionesPermitidas resulta ser un foreign key, en cuyo caso no sería factible que existiese el registro en operacionesPermitidas sin que tenga una correspondencia en operaciones. En un caso así no sería necesario hacer intervenir en el select la tabla operaciones para saber si existe algún registro como resultado del join.

    No sé si LINQ es realmente tan "listo" como para hacer la optimización anterior, pero si lo fuese sería una posible explicación de por qué la tabla no aparece en la select final.

    lunes, 26 de agosto de 2019 15:47
  • hola

    >>Veo que sólo genera un JOIN con la tabla Recurso  ¿qué pasó con el otro join de la tabla Operación?

    Para que necesitas todos esos joins si solo estas aplicando un Any(), quizas si cambias a un ToList() aplique diferente la query o si retornas campos de varias de esas tablas

    si solo usaras

    var query = from opPer in operacionesPermitidas
    			join rec in recursos on new { opPer.ModuloId, opPer.RecursoId } equals new { rec.ModuloId, rec.RecursoId }
    			where rec.Clave == claveRecurso
    			select opPer;

    seria lo mismo, porque solo esas entidades se relacionan, al menos si aplicas el Any()

    saludos


    Leandro Tuttini

    Blog
    MVP Profile
    Buenos Aires
    Argentina

    lunes, 26 de agosto de 2019 18:17
  • Hola,

    La tabla OperacionesPermitidas tiene una llave foránea hacia Operaciones con los campos (ModuloId, RecursoId y OperacionId), estos 3 campos forman la PK de Operaciones. Considero que tienen razón no era necesario el join con la tabla Operaciones ya que sólo estoy validando que exista al menos un recurso (a través de su campo Clave) en OperacionesPermitidas. Lo he modificado como indicas Leandro y va bien.

    var query = from opPer in operacionesPermitidas
    			join rec in recursos on new { opPer.ModuloId, opPer.RecursoId } equals new { rec.ModuloId, rec.RecursoId }
    			where
    				rec.Clave == claveRecurso
    			select opPer;
    
    resultado = query.Any();

    Donde sí necesitaría utilizar el join con Operaciones es en la siguiente consulta:

    var query2 = from opPer in operacionesPermitidas
    			 join op in operaciones on new { opPer.ModuloId, opPer.RecursoId, opPer.OperacionId } equals new { op.ModuloId, op.RecursoId, op.OperacionId }
    			 join rec in recursos on new { opPer.ModuloId, opPer.RecursoId } equals new { rec.ModuloId, rec.RecursoId }
    			 where
    				rec.Clave == claveRecurso &&
    				op.Clave == claveOperacion
    			 select op;
    
    resultado = query2.Any();

    Dado que necesito validar que exista un registro Operaciones de un determinado Recurso validando los campos Clave de ambas tablas y obviamente acá si toma en cuenta ambos joins.

    exec sp_executesql N'SELECT 
        CASE WHEN ( EXISTS (SELECT 
            1 AS [C1]
            FROM   [Seguridad].[RoleOperacion] AS [Extent1]
            INNER JOIN [Seguridad].[Operacion] AS [Extent2] ON ([Extent1].[ModuloId] = [Extent2].[ModuloId]) AND ([Extent1].[RecursoId] = [Extent2].[RecursoId]) AND ([Extent1].[OperacionId] = [Extent2].[OperacionId])
            INNER JOIN [Seguridad].[Recurso] AS [Extent3] ON ([Extent1].[ModuloId] = [Extent3].[ModuloId]) AND ([Extent1].[RecursoId] = [Extent3].[RecursoId])
            WHERE ([Extent3].[Clave] = @p__linq__0) AND ([Extent2].[Clave] = @p__linq__1)
        )) THEN cast(1 as bit) ELSE cast(0 as bit) END AS [C1]
        FROM  ( SELECT 1 AS X ) AS [SingleRowTable1]',N'@p__linq__0 varchar(8000),@p__linq__1 varchar(8000)',@p__linq__0='PS-Admin',@p__linq__1='Edit'

    De momento esto va bien. Lo que estoy pensando es cual sería lo más eficiente. Si dejar el Any como está o cambiarlo por:

    resultado = query.FirstOrDefault() != null;

    Que cumple el mismo cometido para mi caso.

    Saludos y gracias.




    • Editado eduar2083 lunes, 26 de agosto de 2019 22:40
    lunes, 26 de agosto de 2019 22:30
  • Lo que estoy pensando es cual sería lo más eficiente. Si dejar el Any como está o cambiarlo por [FirstOrDefault]

    Es más eficiente el Any. Permite ciertas optimizaciones al no tener que seleccionar información que luego no se va a utilizar, y devolver desde el SQL simplemente un valor de tipo Bit. Por el contrario, el FirstOrDefault tiene que devolver todas las columnas de datos del primer registro, que luego se desperdician y no se usan para nada dado que solamente lo comparas con null.
    martes, 27 de agosto de 2019 6:45