2010-09-21 4 views
9

Dans mon projet, j'utilise EntityFramework 4 pour travailler avec des données. J'ai trouvé d'horribles problèmes de performance avec une requête simple. Quand j'ai regardé le profileur sur une requête sql, généré par EF4, j'ai été choqué.Requêtes Linq dans Entity Framework 4. Performances horribles

J'ai quelques tables dans mon modèle de données d'entité:

Data Model

Il semble assez simple. J'essaie de sélectionner tous les éléments du produit de la catégorie spécifiée avec toutes les propriétés de navigation connexes.

J'ai écrit cette requête LINQ:

ObjectSet<ProductItem> objectSet = ...; 
int categoryId = ...; 

var res = from pi in objectSet.Include("Product").Include("Inventory").Include("Inventory.Storage") 
where pi.Product.CategoryId == categoryId 
select pi; 

EF a généré cette requête SQL:

SELECT [Project1].[pintId1]   AS [pintId], 
[Project1].[pintId]   AS [pintId1], 
[Project1].[intProductId]  AS [intProductId], 
[Project1].[nvcSupplier]  AS [nvcSupplier], 
[Project1].[ nvcArticle]  AS [ nvcArticle], 
[Project1].[nvcBarcode]  AS [nvcBarcode], 
[Project1].[bIsActive]  AS [bIsActive], 
[Project1].[dtDeleted]  AS [dtDeleted], 
[Project1].[pintId2]   AS [pintId2], 
[Project1].[nvcName]   AS [nvcName], 
[Project1].[intCategoryId] AS [intCategoryId], 
[Project1].[ncProductType] AS [ncProductType], 
[Project1].[C1]    AS [C1], 
[Project1].[pintId3]   AS [pintId3], 
[Project1].[intProductItemId] AS [intProductItemId], 
[Project1].[intStorageId]  AS [intStorageId], 
[Project1].[dAmount]   AS [dAmount], 
[Project1].[mPrice]   AS [mPrice], 
[Project1].[dtModified]  AS [dtModified], 
[Project1].[pintId4]   AS [pintId4], 
[Project1].[nvcName1]   AS [nvcName1], 
[Project1].[bIsDefault]  AS [bIsDefault] 
FROM  (SELECT [Extent1].[pintId]   AS [pintId], 
[Extent1].[intProductId] AS [intProductId], 
[Extent1].[nvcSupplier] AS [nvcSupplier], 
[Extent1].[ nvcArticle] AS [ nvcArticle], 
[Extent1].[nvcBarcode]  AS [nvcBarcode], 
[Extent1].[bIsActive]  AS [bIsActive], 
[Extent1].[dtDeleted]  AS [dtDeleted], 
[Extent2].[pintId]   AS [pintId1], 
[Extent3].[pintId]   AS [pintId2], 
[Extent3].[nvcName]  AS [nvcName], 
[Extent3].[intCategoryId] AS [intCategoryId], 
[Extent3].[ncProductType] AS [ncProductType], 
[Join3].[pintId1]   AS [pintId3], 
[Join3].[intProductItemId] AS [intProductItemId], 
[Join3].[intStorageId]  AS [intStorageId], 
[Join3].[dAmount]   AS [dAmount], 
[Join3].[mPrice]   AS [mPrice], 
[Join3].[dtModified]  AS [dtModified], 
[Join3].[pintId2]   AS [pintId4], 
[Join3].[nvcName]   AS [nvcName1], 
[Join3].[bIsDefault]  AS [bIsDefault], 
CASE 
WHEN ([Join3].[pintId1] IS NULL) THEN CAST(NULL AS int) 
ELSE 1 
END AS [C1] 
FROM [ProductItem] AS [Extent1] 
INNER JOIN [Product] AS [Extent2] 
ON [Extent1].[intProductId] = [Extent2].[pintId] 
LEFT OUTER JOIN [Product] AS [Extent3] 
ON [Extent1].[intProductId] = [Extent3].[pintId] 
LEFT OUTER JOIN (SELECT [Extent4].[pintId]   AS [pintId1], 
[Extent4].[intProductItemId] AS [intProductItemId], 
[Extent4].[intStorageId]  AS [intStorageId], 
[Extent4].[dAmount]   AS [dAmount], 
[Extent4].[mPrice]   AS [mPrice], 
[Extent4].[dtModified]  AS [dtModified], 
[Extent5].[pintId]   AS [pintId2], 
[Extent5].[nvcName]   AS [nvcName], 
[Extent5].[bIsDefault]  AS [bIsDefault] 
FROM [Inventory] AS [Extent4] 
INNER JOIN [Storage] AS [Extent5] 
ON [Extent4].[intStorageId] = [Extent5].[pintId]) AS [Join3] 
ON [Extent1].[pintId] = [Join3].[intProductItemId] 
WHERE [Extent2].[intCategoryId] = 8 /* @p__linq__0 */) AS [Project1] 
ORDER BY [Project1].[pintId1] ASC, 
[Project1].[pintId] ASC, 
[Project1].[pintId2] ASC, 
[Project1].[C1] ASC 

Pour 7000 enregistrements dans la base de données et ~ 1000 enregistrement dans la catégorie spécifiée id temps d'exécution de cette requête environ 10 secondes . Rien d'étonnant si regardez ceci:

FROM [ProductItem] AS [Extent1] 
INNER JOIN [Product] AS [Extent2] 
ON [Extent1].[intProductId] = [Extent2].[pintId] 
LEFT OUTER JOIN [Product] AS [Extent3] 
ON [Extent1].[intProductId] = [Extent3].[pintId] 
***LEFT OUTER JOIN (SELECT ....*** 

Nested sélectionner dans ... Horrible ... rejoindre J'ai essayé de changer la requête LINQ, mais je reçois même requête SQL émis.

Une solution utilisant des procédures stockées n'est pas acceptable pour moi, car j'utilise la base de données SQL Compact.

+3

Votre anglais n'est pas si mal :) Question bien formée aussi. +1 – Aren

+1

Vous pouvez utiliser http://imgur.com/ pour partager des images. – Steven

+2

Qu'est-ce qui est inclus? Pourquoi pas juste _from pi dans objectSet où pi.Product.CategoryId == categoryId select pi_? –

Répondre

7

Vous faites Include("Product").Include("Inventory").Include("Inventory.Storage") et vous vous demandez pourquoi tant d'enregistrements sont récupérés et pourquoi voir une telle requête SQL? S'il vous plaît assurez-vous que vous comprenez de quoi traite la méthode Include. Si vous voulez une requête plus simple, s'il vous plaît utiliser les éléments suivants:

var res = 
    from pi in objectSet 
    where pi.Product.CategoryId == categoryId 
    select pi; 

S'il vous plaît noter toutefois que cela possible la charge Products, Inventories et Storages paresseusement, ce qui pourrait causer beaucoup plus de requêtes à envoyer lorsque vous itérer sur ces collections sous .

+0

+ 1 bon point - avec le Product: ProductItem (1: *) et ProductItem: Inventory (1: *), un seul produit sera difficile à comprendre. charger un tas de données supplémentaires (peut-être inutiles) .... pas étonnant qu'il soit lent .... –

0

Je pense que le problème est avec la collection d'inventaire dans l'élément de stockage. Votre requête limitera les éléments Product, ProductItem et Inventory sélectionnés à ceux du paramètre CategoryId spécifié. Toutefois, afin de remplir la collection Inventory de l'élément Storage, la requête doit également renvoyer toutes les lignes Inventory qui utilisent les mêmes StorageId (et ensuite toutes les lignes ProductItem et Product correspondantes pour ces enregistrements Inventory supplémentaires.)

I Commencez par retirer la collection d'inventaire de l'élément de stockage ou retirez l'inclusion correspondante

Questions connexes