Один из способов сделать это сейчас - воспользоваться тем, что F # выполнит это преобразование автоматически при вызове методов для типов .NET, ожидающих Expression<Func<...>>
.
Я не совсем уверенкогда это добавлено к языку, но, конечно, с F # 4, вам не нужно явно преобразовывать выражения F # в выражения LINQ.Если причиной, по которой вы хотели сделать это, была возможность использовать IQueryable
LINQ API (или другие основанные на выражениях .NET API), то теперь он просто работает без усилий, например:
someEfDataContext.MyEntities.Single(fun e -> e.Id = 42)
просто работает.Даже если это выглядит как обычная лямбда (мы не использовали синтаксис выражения F #), она компилируется в код, который создает объект выражения F #, а затем передает его в LeafExpressionConverter.QuotationToExpression
, чтобы превратить его в объект выражения LINQ.
Но иногда вам захочется получить объект выражения в стиле LINQ непосредственно в F #.(Например, иногда полезно написать функцию F #, которая создает выражение, которое вы будете использовать в нескольких запросах.) В этом случае вы можете написать помощник, подобный этому:
type FunAs() =
static member LinqExpression<'T, 'TResult>(e: Expression<Func<'T, 'TResult>>) = e
Это выглядит так, как будтоничего - он просто возвращает свой аргумент.Однако, поскольку FunAs
является типом .NET, F # автоматически скомпилирует любой сайт вызова, который вызывает его с выражением fun
, в код, который генерирует подходящее выражение запроса LINQ.Например:
let linqExpr = FunAs.LinqExpression(fun (e:MyEntity) -> e.Id = 42)
Здесь linqExpr
будет иметь тип Expression<Func<MyEntity, bool>>
.
Ключом к этому является то, что этот метод является членом .NET Type.Если вы попробуете то же самое с обычной функцией F #:
let funAsLinqExpression<'T, 'TResult>(e: Expression<Func<'T, 'TResult>>) = e
, которая, кажется, должна означать то же самое, что и FunAs.LinqExpression
, вы обнаружите, что не можете вызвать ее втак же.Например, если вы попробуете это:
let linqExpr = funAsLinqExpression(fun (e:MyEntity) -> e.Id = 42)
Вы получите (слегка бесполезную) ошибку: «Эта функция принимает слишком много аргументов или используется в контексте, где функция не ожидается».
Сделав эту функцию членом .NET-типа, мы можем воспользоваться полезностью F # "Вы, похоже, вызываете .NET API, который ожидает выражение в стиле LINQ, позвольте мне позаботиться об этом для вас"feature.
(Возможно, существует более явный способ попросить компилятор LINQ выполнить этот же трюк для вас, не добавляя в изображение тип .NET, но я его не нашел.)