Problème de compréhension du code SQL généré à partir de cette requête Entity Framework

J'ai créé un model Entity Framework qui contient deux tables de la database Northwind pour tester certaines de ses fonctionnalités: Produits et CAtegories.

Il a automatiquement créé une association entre Category et Product qui est de 0..1 à *.

J'ai écrit cette requête simple:

var beverages = from p in db.Products.Include("Category") where p.Category.CategoryName == "Beverages" select p; var beverageList = beverages.ToList(); 

J'ai couru le profileur de SQL et ai couru le code ainsi je pourrais voir le SQL qu'il génère et c'est ce qu'il a produit:

 SELECT [Extent1].[ProductID] AS [ProductID], [Extent1].[ProductName] AS [ProductName], [Extent1].[SupplierID] AS [SupplierID], [Extent1].[QuantityPerUnit] AS [QuantityPerUnit], [Extent1].[UnitPrice] AS [UnitPrice], [Extent1].[UnitsInStock] AS [UnitsInStock], [Extent1].[UnitsOnOrder] AS [UnitsOnOrder], [Extent1].[ReorderLevel] AS [ReorderLevel], [Extent1].[Discontinued] AS [Discontinued], [Extent3].[CategoryID] AS [CategoryID], [Extent3].[CategoryName] AS [CategoryName], [Extent3].[Description] AS [Description], [Extent3].[Picture] AS [Picture] FROM [dbo].[Products] AS [Extent1] INNER JOIN [dbo].[Categories] AS [Extent2] ON [Extent1].[CategoryID] = [Extent2].CategoryID] LEFT OUTER JOIN [dbo].[Categories] AS [Extent3] ON [Extent1].[CategoryID] = [Extent3].[CategoryID] WHERE N'Beverages' = [Extent2].[CategoryName] 

Je suis curieux de savoir pourquoi la requête interne se joint aux catégories, puis quitte les jointures. L'instruction select utilise les champs de la table jointe de gauche. Quelqu'un peut-il m'aider à comprendre la raison de cela? Si je supprime la jointure gauche et que je change la list de sélection à extraire de Extent2, j'obtiens les mêmes résultats pour cette requête. Dans quelle situation cela ne serait-il pas vrai?

[Extent3] est une réalisation de Include(Category) et Include ne devrait pas avoir d'impact sur le résultat de la sélection de la table "main" Product, donc LEFT JOIN (tous les loggings de Product et certains loggings de la catégorie right table).

[Extent2] est vraiment de filterr tous les loggings par la table associée Category avec le nom "Beverages", donc dans ce cas, c'est la ressortingction forte ( INNER JOIN )

Pourquoi deux? 🙂 En raison de l'parsing de l'expression par expression et de la génération automatique pour chaque instruction (Inclure, Où)

Vous remarquerez que la requête extrait toutes les colonnes de la list SELECT partir de la copy de l' Extent3 alias Extent3 tableau Catégories, mais qu'elle vérifie le nom de la catégorie par rapport à l' Extent2 .

En d'autres termes, dans ce scénario, la génération de requêtes d'EF ne réalise pas que vous utilisez Include() et que vous limitez la requête via la même table, donc il utilise deux copys à l'aveugle.

Malheureusement, au-delà d'expliquer ce qui se passe, mon expérience avec EF n'est pas assez avancée pour suggérer une solution …

djacobson et igor expliquent assez bien pourquoi cela arrive. La façon dont j'utilise personnellement l'Entity Framework, j'évite d'utiliser Inclure complètement. En fonction de ce que vous envisagez de faire avec datatables, vous pouvez faire quelque chose comme ceci:

 var beverages = from p in db.Products select new {p, p.Category} into pc where pc.Category.CategoryName == "Beverages" select pc; return beverages.ToList().Select(pc => pc.p); 

… qui, au less dans EF 4.0, ne produira qu'une seule jointure interne. Entity Framework est suffisamment intelligent pour que la propriété Category du produit soit remplie avec la catégorie qui est revenue de la database avec celle-ci.

Bien sûr, il est très probable que SQL Server optimise les choses afin que cela ne vous apporte rien.

(Pas directement une réponse à votre question si les requêtes sont les mêmes, mais le champ de commentaire est trop ressortingctif pour cela)

Si vous .Include() le .Include() , ne le charge-t-il pas de toute façon (à cause de l'endroit où)? En général, il est plus logique pour moi d'utiliser des projections au lieu de Include() :

 var beverages = from p in db.Products.Include("Category") where p.Category.CategoryName == "Beverages" select new { Product = p, Category = p.Category }; var beverageList = beverages.ToList();