2015-10-06 100 views
4

在使用提供程序(例如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

回答

4

通常沒有任何方法可以從C#生成同樣的表達式樹。在這種特殊情況下,我認爲這個問題是F#有時插入值類型的防守副本,以防可能發生突變,從而產生實際的報價將是更多的東西一樣

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

這是很不幸的等同,因爲可空類型是不可變的,因此這些防禦副本是不必要的,但是F#編譯器通常不可能確定給定類型是否可變,所以在很多情況下不需要防禦副本。

要解決這個問題,一個選項是定義一個輔助方法來簡化掉,你不需要表達式樹的元素,讓你會打電話像

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