表达式<Func <T,bool >>来自F#func

ham*_*ett 9 linq f#

在linq中,.需要一个Expression>谓词,我可以用F#编写

<@ fun item:'a -> condition @>    // Expr<'a -> bool>
Run Code Online (Sandbox Code Playgroud)

我正在使用FSharp.Powerpack从引用构建表达式,但它给我的是一个MethodCallExpression.看起来很深,powerpack代码正确构建了lambda,但是将它包装在Convert调用中(为什么会这样?).我想知道是否将参数转换为方法调用(一个lambda)最终会给我表达式>我需要.

所以问题是转换调用的原因,以及如何使用Func签名实际获取lambda.

Dan*_*iel 13

我不记得在我发现这段代码的地方,但这是我用来转换Expr<'a -> 'b>为的Expression<Func<'a, 'b>>.希望这将解决您的问题.

open System
open System.Linq.Expressions
open Microsoft.FSharp.Quotations
open Microsoft.FSharp.Linq.QuotationEvaluation

let toLinq (expr : Expr<'a -> 'b>) =
  let linq = expr.ToLinqExpression()
  let call = linq :?> MethodCallExpression
  let lambda = call.Arguments.[0] :?> LambdaExpression
  Expression.Lambda<Func<'a, 'b>>(lambda.Body, lambda.Parameters) 
Run Code Online (Sandbox Code Playgroud)

  • `expr.ToLinqExpression()`现在在`F#`中作为`Microsoft.FSharp.Linq.RuntimeHelpers.LeafExpressionConverter.QuotationToExpression expr` (4认同)

Ian*_*ths 8

现在可以这样做的一种方法是利用F#在调用期望a的.NET类型上的方法时自动执行此转换的事实Expression<Func<...>>.

我不完全确定何时将其添加到语言中,但当然使用F#4时,您不需要将F#表达式显式转换为LINQ表达式.如果您想要首先执行此操作的原因是能够使用IQueryableLINQ API(或其他基于表达式的.NET API),那么现在它可以毫不费力地工作,例如:

someEfDataContext.MyEntities.Single(fun e -> e.Id = 42)
Run Code Online (Sandbox Code Playgroud)

只是工作.虽然这看起来像普通的lambda(我们没有使用F#的表达式语法),但是这会编译生成F#表达式对象的代码,然后将其传递LeafExpressionConverter??.QuotationToExpressi?on给它以将其转换为LINQ表达式对象.

但有时你会想直接在F#中掌握LINQ风格的表达式对象.(例如,有时编写一个F#函数会产生一个你将在多个查询中使用的表达式.)在这种情况下,你可以编写一个这样的帮助器:

type FunAs() =
    static member LinqExpression<'T, 'TResult>(e: Expression<Func<'T, 'TResult>>) = e
Run Code Online (Sandbox Code Playgroud)

看起来它什么都不做 - 它只是返回它的参数.但是,因为FunAs是.NET类型,所以F#会自动编译任何调用它的调用站点,并将fun表达式转换为生成合适的LINQ查询表达式的代码.例如:

let linqExpr = FunAs.LinqExpression(fun (e:MyEntity) -> e.Id = 42)
Run Code Online (Sandbox Code Playgroud)

在这里,linqExpr将是类型Expression<Func<MyEntity, bool>>.

关键是这个方法是.NET Type的成员.如果你使用普通的F#函数尝试完全相同的东西:

let funAsLinqExpression<'T, 'TResult>(e: Expression<Func<'T, 'TResult>>) = e
Run Code Online (Sandbox Code Playgroud)

看起来它应该完全相同FunAs.LinqExpression,你会发现你不能以同样的方式调用它.例如,如果您尝试这样做:

let linqExpr = funAsLinqExpression(fun (e:MyEntity) -> e.Id = 42)
Run Code Online (Sandbox Code Playgroud)

你会得到一个(略微无用的)错误:'这个函数需要太多的参数,或者用在不期望函数的上下文中.

通过使这个函数成为.NET类型的一个成员,我们可以利用F#的有用"您似乎正在调用一个期望LINQ样式表达式的.NET API,让我为您处理"功能.

(有可能有一些更明确的方式要求LINQ编译器为你执行同样的技巧而不将.NET类型带入图片中,但我还没有找到它.)