2012-02-03 11 views
8

w LINQ, .gdzie bierze wyrażenie> predykat, który można napisać w F # jakoExpression <Func <T, bool>> z F # func

<@ fun item:'a -> condition @> // Expr<'a -> bool> 

Używam FSharp.Powerpack zbudować ekspresją z cytat, ale to, co mi daje, to MethodCallExpression. Patrząc głęboko, kod pakunku buduje poprawnie lambdę, ale opakowuje ją w wywołanie Convert (dlaczego tak jest?). Zastanawiam się, czy rzucenie argumentu na wywołanie metody (lambda) w końcu dałoby mi wyrażenie> potrzebuję.

Pytanie więc brzmi, dlaczego wywołanie Convert i jak uzyskać lambdę z podpisem Func.

+0

Hej, znajdujesz prostszy sposób na zrobienie tego? – nicolas

+0

na stronie, jest tu powiązane pytanie (nie z cytatami) http://stackoverflow.com/questions/3392000/interop-between-f-and-c-sharp-lambdas – nicolas

Odpowiedz

12

Nie pamiętam z góry mojej głowy, gdzie znalazłem ten kawałek kodu, ale to jest to, czego używam do konwersji Expr<'a -> 'b> do Expression<Func<'a, 'b>>. Mam nadzieję, że rozwiąże to twój problem.

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) 
+4

'expr.ToLinqExpression() 'jest teraz w' F # 'jako' Microsoft.FSharp.Linq.RuntimeHelpers.LeafExpressionConverter.QuotationToExpression expr' – Maslow

4

Jednym ze sposobów można to zrobić teraz, aby skorzystać z faktu, że F # będą wykonywać tej konwersji automatycznie podczas wywoływania metody na .NET typów, które oczekują Expression<Func<...>>.

Nie jestem całkowicie pewien, kiedy to zostało dodane do języka, ale na pewno z F # 4, nie musisz jawnie konwertować wyrażeń F # na LINQ. Jeśli powód, dla którego chciałem zrobić to w pierwszej kolejności miała być w stanie wykorzystać IQueryable API LINQ (lub inne interfejsy API .NET w oparciu o wyrażenie) to teraz po prostu działa bez wysiłku, np:

someEfDataContext.MyEntities.Single(fun e -> e.Id = 42) 

tylko Prace. Chociaż wygląda to jak zwykła lambda (nie używaliśmy składni wyrażeń F #), to kompiluje się do kodu, który tworzy obiekt ekspresyjny F #, a następnie przekazuje go do LeafExpressionConverter‌​.QuotationToExpressi‌on, aby przekształcić go w obiekt wyrażeń LINQ.

Ale czasami będziesz chciał uzyskać obiekt wyrażenia stylu LINQ bezpośrednio w F #. (Np, czasami warto jest napisać # funkcji F, która produkuje wyrażenie, które będzie używane w wielu zapytań.) W takim przypadku można napisać pomocnika takiego:

type FunAs() = 
    static member LinqExpression<'T, 'TResult>(e: Expression<Func<'T, 'TResult>>) = e 

To wygląda jakby nic nie robi - po prostu zwraca argument. Jednakże, ponieważ FunAs jest typem .NET, F # automatycznie skompiluje dowolną stronę wywołania, która wywoła to z wyrażeniem fun, do kodu, który generuje odpowiednie wyrażenie zapytania LINQ. Np .:

let linqExpr = FunAs.LinqExpression(fun (e:MyEntity) -> e.Id = 42) 

Tutaj linqExpr będzie od rodzaju Expression<Func<MyEntity, bool>>.

Kluczem do tego jest to, że ta metoda należy do typu .NET. Jeśli spróbujesz dokładnie to samo ze zwykłym F # Funkcja:

let funAsLinqExpression<'T, 'TResult>(e: Expression<Func<'T, 'TResult>>) = e 

którym wydaje się, że powinno oznaczać dokładnie to samo, co FunAs.LinqExpression, przekonasz się, że nie można nazwać w ten sam sposób. Na przykład, jeśli spróbujesz to:

let linqExpr = funAsLinqExpression(fun (e:MyEntity) -> e.Id = 42) 

Dostaniesz (nieco nieprzydatne) Błąd: „Funkcja ta zajmuje zbyt wiele argumentów, lub jest używana w kontekście, którego funkcja nie jest expected`.

Dzięki temu, że ta funkcja jest elementem typu .NET, możemy skorzystać z pomocy F # "Wygląda na to, że wywołujesz API .NET, który oczekuje ekspresji w stylu LINQ, pozwól mi zająć się tym dla ciebie" cecha.

(Możliwe, że istnieje jakiś bardziej wyraźny sposób zadawania kompilator LINQ aby wykonać tę samą sztuczkę dla ciebie, nie przynosząc typ .NET na zdjęciu, ale nie znalazłem go.)

Powiązane problemy