Linq Включить вспомогательную функцию для конвейерной обработки в стиле f#

Я хочу с нетерпением загрузить некоторые записи и их отношения из базы данных примерно так:

let getEmails() =
    let emails =
        (query { for q in entities.QueueItems do
                    select q.Email
                    take batchSize }
        ).Include(fun (e:Email) -> e.QueueItem)
        |> Seq.toArray

    emails 
    |> Array.iter (fun e -> entities.QueueItems.Remove(e.QueueItem) |> ignore)

    entities.SaveChanges(logger) |> ignore 
    emails

Это прекрасно работает, хотя мне приходится заключать выражение запроса в скобки, чтобы иметь возможность вызывать для него include, что выглядит немного странно. Я задался вопросом, могу ли я написать вспомогательную функцию для вызова Include в более идиоматичном стиле F#, и я придумал это.

module Ef =
    let Include (f:'a -> 'b) (source:IQueryable<'a>) = 
        source.Include(f)

теперь мой запрос может выглядеть так (вывод типа работает с запрашиваемым типом: D)

let emails =
    query { for q in entities.QueueItems do
                select q.Email
                take batchSize }
    |> Ef.Include(fun e -> e.QueueItem)
    |> Seq.toArray

Он компилируется! Но когда я запускаю его, я получаю сообщение об ошибке из библиотеки DbExtensions, говорящее мне The Include path expression must refer to a navigation property defined on the type.

Проверка лямбда-функции перед ее передачей в Queryable.Include выглядит так {<StartupCode$Service>.$Worker.emails@30} Microsoft.FSharp.Core.FSharpFunc<Entities.Email,Entities.QueueItem> {<StartupCode$Service>.$Worker.emails@30}.

Я предполагаю, что проблема связана с тем, как интерпретируется моя лямбда и преобразованиями между FSharpFuncs и Expression<Func<>>s. Я попытался переписать свою вспомогательную функцию, чтобы она имела Expression<Func<'a, 'b>> в качестве первого параметра, и даже загрузил исходный код FSharp.Core, чтобы найти вдохновение в реализациях модуля Seq и QueryBuilder, но у меня ничего не получилось. Я попытался переопределить свою вспомогательную функцию следующим образом:

module Ef =
    let Include (y:Expression<Func<'a,'b>>) (source:IQueryable<'a>) = 
        source.Include(y)

Но потом я получаю ошибку компилятора This function takes too many arguments, or is used in a context where a function is not expected.

Я немного озадачен. Может ли кто-нибудь предложить, как я могу заставить это работать?


person Joe Taylor    schedule 16.05.2013    source источник


Ответы (1)


Преобразования AFAIR, ориентированные на тип, применяются только к членам типа без карри, а не к привязкам. В качестве исправления вы можете попробовать изменить Ef.Include на статический член.

type Ef = 
    static member Include (f : Expression<System.Func<'a, 'b>>) = 
        fun (q : IQueryable<'a>)  -> q.Include f
person desco    schedule 16.05.2013
comment
Отличное решение! Время пойти и прочитать о конверсиях, направленных на тип. Большое спасибо. - person Joe Taylor; 17.05.2013