如何使用F#提供的LINQ提供程序?

问题描述:

在使用提供程序(例如LINQ到NHibernate)时,在F#中使用LINQ查询的正确方法是什么,以便与C#(相同的AST)中的工作方式相同?如何使用F#提供的LINQ提供程序?

我的具体问题是,将查询转换为F#会在C#工作时抛出错误。这可能是由于F#没有生成相同的AST引起的。 Roslyn为C#提供了一个Visual Studio AST可视化扩展,但我不知道任何F#的AST查看器。

具有以下工作C#查询:

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

当转换为F#:

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

失败,出现以下错误:

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 

使用.First(fun someEntity -> someEntity.SomeReferenceType <> null)作品虽然正确,这导致了上面的结论:AST在cas中产生不同使用.HasValue

通常没有任何方法可以从C#生成同样的表达式树。在这种特殊情况下,我认为这个问题是F#有时插入值类型的防守副本,以防可能发生突变,从而产生实际的报价将是更多的东西一样

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

这是很不幸的等同,因为可空类型是不可变的,因此这些防御副本是不必要的,但是F#编译器通常不可能确定给定类型是否可变,所以在很多情况下不需要防御副本。

要解决这个问题,一个选项是定义一个辅助方法来简化掉,你不需要表达式树的元素,让你会打电话像

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