Remarque: J'ai écrit cette réponse lorsque Entity Framework 4 était réel. Le but de cette réponse n'était pas d'entrer dans des tests triviaux .Any()
contre des .Count()
performances. Le but était de signaler que EF est loin d'être parfait. Les versions plus récentes sont meilleures ... mais si vous avez une partie de code qui est lente et utilise EF, testez avec TSQL direct et comparez les performances plutôt que de vous fier à des hypothèses (c'est .Any()
TOUJOURS plus rapide que .Count() > 0
).
Bien que je sois d'accord avec la réponse et les commentaires les plus votés - en particulier sur le point Any
, l' intention du développeur est meilleure queCount() > 0
- j'ai eu une situation dans laquelle Count est plus rapide par ordre de grandeur sur SQL Server (EntityFramework 4).
Voici la requête avec Any
cette exception de délai d'attente (sur environ 200 000 enregistrements):
con = db.Contacts.
Where(a => a.CompanyId == companyId && a.ContactStatusId <= (int) Const.ContactStatusEnum.Reactivated
&& !a.NewsletterLogs.Any(b => b.NewsletterLogTypeId == (int) Const.NewsletterLogTypeEnum.Unsubscr)
).OrderBy(a => a.ContactId).
Skip(position - 1).
Take(1).FirstOrDefault();
Count
version exécutée en quelques millisecondes:
con = db.Contacts.
Where(a => a.CompanyId == companyId && a.ContactStatusId <= (int) Const.ContactStatusEnum.Reactivated
&& a.NewsletterLogs.Count(b => b.NewsletterLogTypeId == (int) Const.NewsletterLogTypeEnum.Unsubscr) == 0
).OrderBy(a => a.ContactId).
Skip(position - 1).
Take(1).FirstOrDefault();
J'ai besoin de trouver un moyen de voir quel SQL exact les deux LINQ produisent - mais il est évident qu'il y a une énorme différence de performances entre Count
et Any
dans certains cas, et malheureusement il semble que vous ne pouvez pas simplement vous en tenir Any
à tous les cas.
EDIT: Voici les SQL générés. Des beautés comme vous pouvez le voir;)
ANY
:
exec sp_executesql N'SELECT TOP (1)
[Project2]. [ContactId] AS [ContactId],
[Project2]. [CompanyId] AS [CompanyId],
[Project2]. [ContactName] AS [ContactName],
[Project2]. [FullName] AS [FullName],
[Project2]. [ContactStatusId] AS [ContactStatusId],
[Project2]. [Créé] AS [Créé]
FROM (SELECT [Project2]. [ContactId] AS [ContactId], [Project2]. [CompanyId] AS [CompanyId], [Project2]. [ContactName] AS [ContactName], [Project2]. [FullName] AS [FullName] , [Project2]. [ContactStatusId] AS [ContactStatusId], [Project2]. [Created] AS [Created], row_number () OVER (ORDER BY [Project2]. [ContactId] ASC) AS [row_number]
DE (CHOISIR
[Extent1]. [ContactId] AS [ContactId],
[Extent1]. [CompanyId] AS [CompanyId],
[Extent1]. [ContactName] AS [ContactName],
[Extent1]. [FullName] AS [FullName],
[Extent1]. [ContactStatusId] AS [ContactStatusId],
[Extent1]. [Créé] AS [Créé]
DE [dbo]. [Contact] AS [Extent1]
OERE ([Extent1]. [CompanyId] = @ p__linq__0) ET ([Extent1]. [ContactStatusId] <= 3) ET (PAS EXISTANT (SELECT
1 AS [C1]
DE [dbo]. [NewsletterLog] AS [Extent2]
OERE ([Extent1]. [ContactId] = [Extent2]. [ContactId]) ET (6 = [Extent2]. [NewsletterLogTypeId])
))
) AS [Project2]
) AS [Project2]
OERE [Project2]. [Row_number]> 99
COMMANDER PAR [Project2]. [ContactId] ASC ', N' @ p__linq__0 int ', @ p__linq__0 = 4
COUNT
:
exec sp_executesql N'SELECT TOP (1)
[Project2]. [ContactId] AS [ContactId],
[Project2]. [CompanyId] AS [CompanyId],
[Project2]. [ContactName] AS [ContactName],
[Project2]. [FullName] AS [FullName],
[Project2]. [ContactStatusId] AS [ContactStatusId],
[Project2]. [Créé] AS [Créé]
FROM (SELECT [Project2]. [ContactId] AS [ContactId], [Project2]. [CompanyId] AS [CompanyId], [Project2]. [ContactName] AS [ContactName], [Project2]. [FullName] AS [FullName] , [Project2]. [ContactStatusId] AS [ContactStatusId], [Project2]. [Created] AS [Created], row_number () OVER (ORDER BY [Project2]. [ContactId] ASC) AS [row_number]
DE (CHOISIR
[Project1]. [ContactId] AS [ContactId],
[Project1]. [CompanyId] AS [CompanyId],
[Project1]. [ContactName] AS [ContactName],
[Project1]. [FullName] AS [FullName],
[Project1]. [ContactStatusId] AS [ContactStatusId],
[Project1]. [Créé] AS [Créé]
DE (CHOISIR
[Extent1]. [ContactId] AS [ContactId],
[Extent1]. [CompanyId] AS [CompanyId],
[Extent1]. [ContactName] AS [ContactName],
[Extent1]. [FullName] AS [FullName],
[Extent1]. [ContactStatusId] AS [ContactStatusId],
[Extent1]. [Créé] COMME [Créé],
(SÉLECTIONNER
COUNT (1) AS [A1]
DE [dbo]. [NewsletterLog] AS [Extent2]
OERE ([Extent1]. [ContactId] = [Extent2]. [ContactId]) ET (6 = [Extent2]. [NewsletterLogTypeId])) AS [C1]
DE [dbo]. [Contact] AS [Extent1]
) AS [Project1]
OERE ([Project1]. [CompanyId] = @ p__linq__0) AND ([Project1]. [ContactStatusId] <= 3) AND (0 = [Project1]. [C1])
) AS [Project2]
) AS [Project2]
OERE [Project2]. [Row_number]> 99
COMMANDER PAR [Project2]. [ContactId] ASC ', N' @ p__linq__0 int ', @ p__linq__0 = 4
Il semble que pur Where avec EXISTS fonctionne bien pire que de calculer Count puis de faire Where with Count == 0.
Faites-moi savoir si vous voyez des erreurs dans mes conclusions. Ce qui peut être retiré de tout cela indépendamment de la discussion Any vs Count, c'est que tout LINQ plus complexe est beaucoup mieux lorsqu'il est réécrit en tant que procédure stockée;).