2015-10-06 2 views
4

Quelle est la bonne façon d'utiliser les requêtes LINQ en F # lors de l'utilisation d'un fournisseur (LINQ to NHibernate par exemple) afin de travailler de la même manière qu'en C# (même AST)?Comment utiliser un fournisseur LINQ à partir de F #?

Mon problème spécifique est que la traduction d'une requête en F # génère une erreur alors que celle en C# fonctionne. Cela peut être dû au fait que F # ne génère pas le même AST. Roslyn fournit une extension Visual Studio AST pour C#, mais je ne connais aucun visualiseur AST pour F #.

Ayant le travail suivant C# requête:

.First(someEntity => someEntity.SomeNullableInt.HasValue); 

quand il est traduit à F #:

.First(fun someEntity -> someEntity.SomeNullableInt.HasValue) 

il échoue avec l'erreur suivante:

System.NotSupportedException: Boolean Invoke(System.Nullable`1[System.Int32]) 
> at NHibernate.Linq.Visitors.HqlGeneratorExpressionTreeVisitor.VisitMethodCallExpression(MethodCallExpression expression) 
    at NHibernate.Linq.Visitors.QueryModelVisitor.VisitWhereClause(WhereClause whereClause, QueryModel queryModel, Int32 index) 
    at Remotion.Linq.QueryModelVisitorBase.VisitBodyClauses(ObservableCollection`1 bodyClauses, QueryModel queryModel) 
    at Remotion.Linq.QueryModelVisitorBase.VisitQueryModel(QueryModel queryModel) 
    at NHibernate.Linq.Visitors.QueryModelVisitor.GenerateHqlQuery(QueryModel queryModel, VisitorParameters parameters, Boolean root) 
    at NHibernate.Linq.NhLinqExpression.Translate(ISessionFactoryImplementor sessionFactory, Boolean filter) 
    at NHibernate.Hql.Ast.ANTLR.ASTQueryTranslatorFactory.CreateQueryTranslators(IQueryExpression queryExpression, String collectionRole, Boolean shallow, IDictionary`2 filters, ISessionFactoryImplementor factory) 
    at NHibernate.Engine.Query.QueryPlanCache.GetHQLQueryPlan(IQueryExpression queryExpression, Boolean shallow, IDictionary`2 enabledFilters) 
    at NHibernate.Impl.AbstractSessionImpl.GetHQLQueryPlan(IQueryExpression queryExpression, Boolean shallow) 
    at NHibernate.Impl.AbstractSessionImpl.CreateQuery(IQueryExpression queryExpression) 
    at NHibernate.Linq.DefaultQueryProvider.PrepareQuery(Expression expression, IQuery& query, NhLinqExpression& nhQuery) 
    at NHibernate.Linq.DefaultQueryProvider.Execute(Expression expression) 
    at NHibernate.Linq.DefaultQueryProvider.Execute[TResult](Expression expression) 
    ... 
Stopped due to error 

L'utilisation .First(fun someEntity -> someEntity.SomeReferenceType <> null) fonctionne correctement si, ce qui conduit à la conclusion ci-dessus: AST est généré différemment dans le cas e d'utiliser .HasValue.

Répondre

4

Il n'existe généralement aucun moyen de générer le même arbre d'expression à partir de F # que vous le pouvez à partir de C#. Dans ce cas particulier, je pense que la question est que F # insère parfois des copies de défense des types de valeur pour protéger contre les mutations possibles de sorte que la cotation réelle générée sera l'équivalent de quelque chose comme

someEntity => ((System.Func<bool?,bool>)(copyOfNullable => copyOfNullable.HasValue)).Invoke(someEntity.SomeNullableInt) 

Cela est regrettable, parce que annulable les types sont immuables donc ces copies défensives sont inutiles, mais il n'est généralement pas possible pour le compilateur F # de déterminer si un type donné est mutable ou non, donc des copies défensives sont ajoutées dans de nombreux cas où elles ne sont pas strictement nécessaires.

Pour contourner ce problème, une option est de définir une méthode d'aide pour simplifier les éléments d'arbre d'expression de suite que vous n'avez pas besoin, de sorte que vous appelleriez quelque chose comme

.First(Simplify(fun someEntity -> someEntity.SomeNullableInt.HasValue))