2009-05-27 5 views
4

J'ai le bit de code suivant:Comment créer une expression LINQ à partir d'une propriété de navigation Entity Framework?

Expression<Func<Subscription, Service>> service2= subscription => (from relationship in subscription.ChildRelationships 
    select relationship.SecondService).FirstOrDefault(); 

Il crée une expression que je peux utiliser plus tard dans le cadre d'une requête avec le cadre de l'entité. Le code que j'utilise a une clause where mais je l'ai omis pour la lisibilité. Cela fonctionne très bien et je suis capable de l'exécuter dans LINQPad que j'utilise pour tester.

Si je change le code:

Expression<Func<Subscription, IQueryable<Service>>> service2= subscription => (from relationship in subscription.ChildRelationships 
    select relationship.SecondService); 

ne plus Compile et a l'erreur suivante:

Cannot convert lambda expression to delegate type 'System.Func<Farmworks.Data.Subscription,System.Linq.IQueryable<Farmworks.Data.Service>>' because some of the return types in the block are not implicitly convertible to the delegate return type

Il semble être parce ChildRelationships qui est une propriété de navigation ne met pas en œuvre IQueryable. Il est en fait de type EntityCollection qui implémente IEnumerable mais n'est pas bon pour créer des expressions qui fonctionnent avec EF. Je pense que je comprends pourquoi le deuxième bloc de code ne fonctionne pas et aimerait savoir comment le réécrire de telle sorte que cela fonctionne. Ce qui m'intrigue aussi, c'est pourquoi le premier bloc de code fonctionne. Il utilise également la propriété de navigation ChildRelationships mais n'a aucun problème pour devenir une expression qui fonctionne avec EF.

Quelqu'un peut-il jeter de la lumière?

Répondre

0

Je pense que CompiledQuery classe peut vous aider dans ce cas:

Expression<Func<Subscription, IQueryable<Service>>> service2 = 
    CompiledQuery.Compile( 
     subscription => (
      from relationship in subscription.ChildRelationships 
      select relationship.SecondService 
     ) 
    ); 
1

La question n'est pas FirstOrDefault fait en quelque sorte le travail d'expression; c'est, comme vous le dites, que ChildRelationships n'implémente pas en soi IQueryable. Vous pouvez contourner cela de deux façons différentes, selon vos besoins.

Vous pouvez utiliser la méthode IEnumerable.AsQueryable. C'est probablement le meilleur si les services sont déjà chargés depuis le contexte.

Si les services ne sont pas chargés, vous pouvez utiliser les méthodes Load ou Include, au besoin, pour les charger au moment opportun, puis utiliser la solution ci-dessus.

Si vous avez une référence à votre ObjectContext, vous pouvez utiliser ObjectQuery, qui n'implémente IQueryable:

Expression<Func<Subscription, MyEntities, IQueryable<Service>>> service2 = 
    (subscription, context) => (
    from s in context.Subscriptions // here is the IQueryable 
    where s.Id == subscription.Id 
    from relationship in s.ChildRelationships 
    select relationship.SecondService); 
+0

Essayé le code mais obtenu le suivant: Une expression de type 'Data.Service' n'est pas autorisée dans une clause from dans une expression de requête avec le type de source 'System.Data.Objects.DataClasses.EntityCollection '. L'inférence de type a échoué dans l'appel de 'SelectMany'. – GiddyUpHorsey

+0

OK, je réalise ce que sont les types ici. Je vais réécrire ma réponse. –

0

Avez-vous besoin pour être IQueryable < Service>? Je pense que select renvoie un service IEnumerable <>. LINQ fonctionne directement sur IEnumerables, donc vous pouvez simplement déclarer service2 comme Expression < Func < Abonnement, IEnumerable < Service >>>.

Questions connexes