2011-01-18 36 views
8

Después de crear perfiles en mi capa de base de datos basada en Entity Framework 4.0 encontré que el principal peinador de rendimiento es un LINQ simple() Utilizo para verificar si una entidad ya existe en la base de datos. El control Any() realiza órdenes de magnitud más lentas que el guardado de la entidad. Hay relativamente pocas filas en la base de datos y las columnas que se controlan están indexadas.Optimizar llamada LINQ Any() en Entity Framework

utilizo el siguiente LINQ para comprobar la existencia de un grupo de ajustes:

from sg in context.SettingGroups 
where sg.Group.Equals(settingGroup) && sg.Category.Equals(settingCategory) 
select sg).Any() 

Esto genera el siguiente código SQL (además, mi SQL afirma que la consulta se ejecuta dos veces):

exec sp_executesql N'SELECT 
CASE WHEN (EXISTS (SELECT 
    1 AS [C1] 
    FROM [dbo].[SettingGroups] AS [Extent1] 
    WHERE ([Extent1].[Group] = @p__linq__0) AND ([Extent1].[Category] = @p__linq__1) 
)) THEN cast(1 as bit) WHEN (NOT EXISTS (SELECT 
    1 AS [C1] 
    FROM [dbo].[SettingGroups] AS [Extent2] 
    WHERE ([Extent2].[Group] = @p__linq__0) AND ([Extent2].[Category] = @p__linq__1) 
)) THEN cast(0 as bit) END AS [C1] 
FROM (SELECT 1 AS X) AS [SingleRowTable1]',N'@p__linq__0 nvarchar(4000),@p__linq__1 nvarchar(4000)',@p__linq__0=N'Cleanup',@p__linq__1=N'Mediator' 

Ahora solo puedo pensar en crear procedimientos almacenados para resolver este problema, pero por supuesto preferiría mantener el código en LINQ.

¿Hay alguna manera de hacer que una comprobación "Exista" se ejecute más rápido con EF?

Probablemente debería mencionar que también uso auto-tracking-entities en una arquitectura de n niveles. En algunos escenarios, el estado ChangeTracker para algunas entidades se establece en "Agregado" a pesar de que ya existen en la base de datos. Esta es la razón por la que uso un cheque para cambiar el estado de ChangeTracker en consecuencia si la actualización de la base de datos causó una excepción de falla de inserción.

Respuesta

0

El problema es que Entity Framework (al menos EF4) está generando SQL estúpido. El siguiente código parece generar SQL decente con un mínimo de dolor.

public static class LinqExt 
{ 
    public static bool BetterAny<T>(this IQueryable<T> queryable, Expression<Func<T, bool>> predicate) 
    { 
     return queryable.Where(predicate).Select(x => (int?)1).FirstOrDefault().HasValue; 
    } 

    public static bool BetterAny<T>(this IQueryable<T> queryable) 
    { 
     return queryable.Select(x => (int?)1).FirstOrDefault().HasValue; 
    } 

} 

A continuación, puede hacer:

(from sg in context.SettingGroups 
where sg.Group.Equals(settingGroup) && sg.Category.Equals(settingCategory) 
select sg).BetterAny() 

o incluso:

context.SettingGroups.BetterAny(sg => sg.Group.Equals(settingGroup) && sg.Category.Equals(settingCategory)); 
0

Sé que suena una solución miserable, pero ¿qué ocurre si usa Count en vez de Any?

+0

voy a darle una oportunidad. – Holstebroe

0

¿Ha perfilado el tiempo para ejecutar la instrucción de selección generada contra el tiempo para ejecutar la selección de lo que usted esperaría/desea que se produzca? Es posible que no sea tan malo como parece.

La sección

SELECT 
1 AS [C1] 
FROM [dbo].[SettingGroups] AS [Extent1] 
WHERE ([Extent1].[Group] = @p__linq__0) AND ([Extent1].[Category] = @p__linq__1) 

es probablemente cerca de lo que se puede esperar a ser producido. Es muy posible que el optimizador de consultas se dé cuenta de que la segunda consulta es la misma que la primera y, por lo tanto, puede agregar muy poco tiempo a la consulta general.

1

Intente agregar índice a la tabla de la base de datos "SettingGroups", por grupo & Categoría.

BTW, ¿esto produce sql similar?

var ok = context.SettingGroups.Any(sg => sg.Group==settingGroup && sg.Category==settingCategory); 
+0

Sí, use un índice. Crear índice IX_SettingGroups_GC en SettingGroups (Group, Category) – Ben

Cuestiones relacionadas