2010-12-11 73 views
2

En utilisant les produits de la base de données AdventureWorks table comme exemple, j'ai créé un DBML et étendu les propriétés du DataContext pour inclure une nouvelle propriété:Exception LINQ: "Les requêtes avec des collections locales ne sont pas prises en charge." mais pas en utilisant une collection locale

 
partial class AdventureWorksDataContext 
{ 
    public IQueryable<Product> FinishedProducts 
    { 
    get { return Products.Where(p => p.FinishedGoodsFlag); } 
    } 
} 

La propriété des produits fait partie de la DataContext généré et tout ce que j'ai fait est d'ajouter un à partir de la table <produit> de sorte qu'il renvoie un IQueryable. Maintenant

, le problème vient en essayant de l'interroger comme celui-ci (par exemple muet, mais qui devrait montrer le problème):

var queryFinishedProducts = datacontext.FinishedProducts.Where(fp => fp.ProductID == datacontext.FinishedProducts.Max(p => p.ProductID));

Itération ce résultat de la requête dans « Les requêtes avec des collections locales ne sont pas supporté "exception. Je ne comprends pas pourquoi il lancerait cette erreur quand il n'y a pas de collections locales utilisées. Si je le lance sur la table normale des produits (qui est un Table<Product>):

var queryProducts = datacontext.FinishedProducts.Where(fp => fp.ProductID == datacontext.Products.Max(p => p.ProductID));

... ça fonctionne très bien. La seule différence est que j'ai ajouté un Table<Product> et l'a renvoyé comme IQueryable<Product>.

Vous avez des idées?

+1

Est-ce que FinishedProducts est une propriété? Où est le "get"? –

+0

Oui, c'est une propriété. Désolé, je vais ajouter le get pour plus de clarté. – Mike

+0

Il semblerait que le générateur SQL n'ait pas pu dire que le 'Products' utilisé dans la requête de FinishedProducts provenait du même' DataContext' que le reste de la requête. – Gabe

Répondre

0

est ici une approche qui devrait fonctionner:

var max = datacontext.FinishedProducts.Max(p => p.ProductID); 
var queryFinishedProducts = datacontext.FinishedProducts 
             .Where(fp => fp.ProductID == max); 

Ou en supposant que ProductID est d'essayer unique de réécriture des requêtes comme suit:

var queryProducts = datacontext.FinishedProducts 
           .OrderByDescending(p => p.ProductID) 
           .First(); 
+1

Merci pour la réponse, mais je cherchais plus pour une raison pour laquelle LINQ traiterait différemment ce qui est essentiellement la même requête. La requête était vraiment juste un exemple d'un problème plus important, le lambda étant capable d'utiliser ce qui est essentiellement un sous-SELECT. – Mike

+0

@Mike: Je pense que la différence est parce que 'Products' n'est pas seulement un' IQueryable ', c'est un' Table '. –

+0

J'ai creusé dans les entrailles des classes Table et DataQuery avec Reflector et Table implémente simplement les méthodes CreateQuery et retourne une requête DataQuery chaque fois qu'une méthode d'extension est utilisée. Cela dit, ceci: Products.Where (p => p) devrait être égal à une propriété qui retourne la même déclaration, ce que montre l'exemple. Et c'est ce que je ne comprends pas, c'est comment faire passer un IQueryable en utilisant la propriété Products est différent. – Mike

0

j'ai pu reproduire ce comportement. Voici quelques autres types à regarder dans le réflecteur:

System.Data.Linq.SqlClient.SqlBinder.Visitor 
System.Data.Linq.SqlClient.SqlVisitor 

Basé sur cette trace de pile.

at System.Data.Linq.SqlClient.SqlBinder.Visitor.ConvertToFetchedSequence(SqlNode node) 
at ..SqlBinder.Visitor.VisitAlias(SqlAlias a) 
at System.Data.Linq.SqlClient.SqlVisitor.Visit(SqlNode node) 
at ..SqlVisitor.VisitSource(SqlSource source) 
at ..SqlBinder.Visitor.VisitSelect(SqlSelect select) 
at ..SqlVisitor.Visit(SqlNode node) 
at ..SqlBinder.Visitor.VisitAlias(SqlAlias a) 
at ..SqlVisitor.Visit(SqlNode node) 
at ..SqlVisitor.VisitSource(SqlSource source) 
at ..SqlBinder.Visitor.VisitSelect(SqlSelect select) 
at ..SqlVisitor.Visit(SqlNode node) 
at ..SqlVisitor.VisitSequence(SqlSelect sel) 
at ..SqlVisitor.VisitScalarSubSelect(SqlSubSelect ss) 
at ..SqlVisitor.VisitSubSelect(SqlSubSelect ss) 
at ..SqlBinder.Visitor.VisitSubSelect(SqlSubSelect ss) 

Je me demande pourquoi une propriété de type Table<T> est traitée différemment d'une propriété de type IQueryable<T> ou même ITable<T>. L'implémentation de la propriété n'a pas d'importance, le type de retour est important.


Une propriété de type IQueryable<T> est vraiment traités différemment une variable scope localement le type IQueryable<T>

IQueryable<Customer> query1 = 
    myDC.Customers.Where(c => c.ID == myDC.CoolCustomers.Max(c2 => c2.ID)); 

IQueryable<Customer> query2 = 
    myDC.Customers.Where(c => c.ID == myDC.Customers.Where(c2 => c2.Flag).Max(c2 => c2.ID)); 

IQueryable<Customer> subQuery = myDC.CoolCustomers; 
IQueryable<Customer> query3 = 
    myDC.Customers.Where(c => c.ID == subQuery.Max(c2 => c2.ID)); 

query1 présente le comportement d'origine (exception, des séquences locales).

query2 génère ce sql - moins que l'idéal.

SELECT [t0].[ID], [t0].[Flag] 
FROM [Customer] AS [t0] 
OUTER APPLY (
    SELECT MAX([t1].[ID]) AS [value] 
    FROM [Customer] AS [t1] 
    WHERE [t1].[Flag] = 1 
    ) AS [t2] 
WHERE [t0].[ID] = [t2].[value] 

requete3 sur-avidement émet la sous-requête lors de la traduction de la requête, fait alors un deuxième aller-retour pour la requête principale.

+0

Merci d'avoir creusé aussi profondément dans le framework LINQ! Je souhaite juste que j'ai compris pourquoi c'est traité différemment. Je vais commencer à creuser ces classes et voir ce que je peux trouver d'autre. Espérons vraiment que ce n'est pas la première fois que cela se présente, il semble que quelque chose de grand projet pourrait faire pour pré-filtrer les propriétés de la table. – Mike