2011-01-18 14 views
8

Dopo aver profilato il layer di database basato su Entity Framework 4.0, ho trovato che il principale vantaggio delle prestazioni è un semplice LINQ Any() che utilizzo per verificare se un'entità è già presente nel database. Il controllo Qualsiasi() esegue ordini di grandezza più lenti del salvataggio dell'entità. Ci sono relativamente poche righe nel database e le colonne controllate sono indicizzate.Ottimizzazione della chiamata LINQ Any() in Entity Framework

Io uso il seguente LINQ per verificare l'esistenza di un gruppo di impostazioni:

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

Questo genera il seguente SQL (in aggiunta mio profiler SQL sostiene la query viene eseguita due volte):

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' 

In questo momento posso solo pensare di creare stored procedure per risolvere questo problema, ma ovviamente preferirei mantenere il codice in LINQ.

C'è un modo per rendere più veloce un controllo "Esistente" con EF?

Probabilmente dovrei menzionare che uso anche le entità di auto-tracciamento in un'architettura a più livelli. In alcuni scenari, lo stato di ChangeTracker per alcune entità è impostato su "Aggiunto" anche se sono già presenti nel database. Questo è il motivo per cui utilizzo un controllo per modificare lo stato di ChangeTracker di conseguenza se l'aggiornamento del database ha causato un'eccezione di errore dell'inserto.

risposta

0

Il problema è Entity Framework (almeno EF4) sta generando stupido SQL. Il seguente codice sembra generare SQL decente con dolore minimo.

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; 
    } 

} 

Poi si può fare:

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

o anche:

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

So che sembra una soluzione infelice, ma cosa succede se usi Count invece di Any?

+0

io vi darò una prova. – Holstebroe

0

Avete profilato il tempo di eseguire l'istruzione di selezione generata rispetto al tempo di esecuzione della selezione di ciò che vi aspettereste/mi piacerebbe produrre? È possibile che non sia così male come sembra.

La sezione

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

è probabilmente vicino a quello che ci si aspetta da produrre. È abbastanza probabile che il Query Optimiser realizzi che la seconda query è la stessa della prima e quindi potrebbe aggiungere pochissimo tempo alla query generale.

1

Provare ad aggiungere l'indice alla tabella del database "SettingGroups", per Gruppo & Categoria.

BTW, questo produce sql simile?

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

Sì, utilizzare un indice. Creare l'indice IX_SettingGroups_GC su SettingGroups (Group, Category) – Ben

Problemi correlati