Композиционные выражения FLinq - PullRequest
3 голосов
/ 26 апреля 2010

При выполнении linq-to-sql в c # вы можете сделать что-то вроде этого:

var data = context.MyTable.Where(x => x.Parameter > 10); 

var q1 = data.Take(10); 
var q2 = data.Take(3); 

q1.ToArray(); 
q2.ToArray(); 

Это сгенерирует 2 отдельных запроса SQL, один с ТОП 10, а другой с ТОП 3. В процессе игры с Flinq я вижу, что:

let data = query <@ seq { for i in context.MyTable do if x.Parameter > 10 then yield i } @> 

data |> Seq.take 10 |> Seq.toList 
data |> Seq.take 3 |> Seq.toList 

не делает то же самое. Здесь он, кажется, выполняет один полный запрос, а затем выполняет вызовы «take» на стороне клиента. Альтернатива, которую я вижу, используется:

let q1 = query <@ for i in context.MyTable do if x.Param > 10 then yield i } |> Seq.take 10 @> 
let q2 = query <@ for i in context.MyTable do if x.Param > 10 then yield i } |> Seq.take 3 @> 

Эти 2 генерируют SQL с соответствующим фильтром TOP N. Моя проблема с этим состоит в том, что это не кажется составным. По сути, мне нужно продублировать предложение "where", и, возможно, мне придется продублировать другие подзапросы, которые я мог бы выполнить для базового запроса. Есть ли способ, чтобы F # дал мне что-то более сочетаемое?

(я изначально отправил этот вопрос на hubfs , где я получил несколько ответов, касающихся того факта, что C # выполняет преобразование запроса «в конце», то есть когда нужны данные, где F # делает это преобразование с нетерпением.)

1 Ответ

5 голосов
/ 26 апреля 2010

Чтобы сделать это в F #, вам нужно использовать немного другой подход. Вместо того, чтобы составлять запрос с использованием вызовов методов (и отложенного выполнения), вам нужно будет создать цитируемый код F #. Если вам просто необходимо параметризовать код с помощью числового аргумента, вы можете написать функцию, которая выполняет запрос:

let takeData count = 
  <@ seq { for i in context.MyTable do 
             if x.Parameter > 10 then 
               yield i }
     |> Seq.take count @> |> query

Это работает только в простых случаях, потому что параметр может быть только числом. Однако вы также можете составлять цитаты F # таким образом, чтобы вы могли добавлять другие операции в основной запрос.

Например, допустим, вы хотите добавить либо Seq.take, либо Seq.sortBy к основной части запроса. Это можно сделать с помощью так называемого оператора сплайсинга . Внутри цитат (код в <@ .. @>) вы можете использовать специальный оператор %, который позволяет склеивать другую цитату в ту, которую вы строите:

let createQuery op = 
  <@ seq { for i in context.MyTable do 
             if x.Parameter > 10 then 
               yield i }
     |> %op @> |> query

Здесь параметр op имеет тип Expr<seq<MyTableRow> -> 'a>. Вы можете вызвать функцию createQuery с некоторой кавычкой в ​​качестве аргумента, и она добавит аргумент после основного запроса. Например:

createQuery <@ Seq.take 10 @>
createQuery <@ Seq.sortBy (fun x -> x.Parameter) @>

Это на самом деле намного мощнее, чем то, что позволяет вам делать C #. Я написал две статьи об этом некоторое время назад:

  • Составление запросов LINQ во время выполнения в F # показывает несколько примеров F # - к сожалению, он использует устаревший синтаксис, поэтому он не будет работать напрямую, но он должен продемонстрировать идеи. В более старых версиях F # вы могли использовать _ в цитате, которая автоматически создала функцию, которая взяла цитату (которая будет вставлена ​​вместо _ в качестве аргумента). Это должно быть переписано с использованием (вам также больше не нужны забавные символы Юникода: -)):

    (fun x -> <@ .. %x .. @>)
    
  • Составление запросов LINQ во время выполнения в C # показывает, как предоставить некоторые дополнительные функции, которые непосредственно не доступны в C # (используя некоторые приемы)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...