【问题标题】:How to use a LINQ provider from F#?如何使用 F# 中的 LINQ 提供程序?
【发布时间】:2015-10-06 10:38:27
【问题描述】:

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

我的具体问题是将查询转换为 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) 可以正常工作,这导致了上面的结论:在使用.HasValue 的情况下,AST 的生成方式不同。

【问题讨论】:

    标签: c# linq nhibernate f# custom-linq-providers


    【解决方案1】:

    通常没有任何方法可以从 F# 生成与从 C# 生成相同的表达式树。在这种特殊情况下,我认为问题在于 F# 有时会插入值类型的防御性副本以防止可能发生的突变,因此生成的实际引用将相当于类似于

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

    这很不幸,因为可空类型是不可变的,因此这些防御性副本是不必要的,但 F# 编译器通常无法确定给定类型是否可变,因此在许多情况下添加防御性副本而不是它们非常需要。

    要解决这个问题,一种选择是定义一个辅助方法来简化不需要的表达式树元素,这样你就可以调用类似

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-05-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多