I have tried to use FLINQ but it is rather out of date with F# 3.0 beta.
Can someone give me some pointers on how to create dynamic SQL queries in F#?
I have tried to use FLINQ but it is rather out of date with F# 3.0 beta.
Can someone give me some pointers on how to create dynamic SQL queries in F#?
We have recently developed a library, FSharpComposableQuery
, aimed at supporting more flexible composition of query expressions in F# 3.0 and above. It's intended as a drop-in replacement overloading the standard query builder.
Tomas's example can be modified as follows:
open FSharpComposableQuery
// Initial query that simply selects products
let q1 =
<@ query { for p in ctx.Products do
select p } @>
// Create a new query that specifies only expensive products
let q2 =
query { for p in %q1 do
where (p.UnitPrice.Value > 100.0M) }
This simply quotes the query expression and splices it into the second query. However, this results in a quoted query expression that the default QueryBuilder
may not be able to turn into a single query, because q2
evaluates to the (equivalent) expression
query { for p in (query { for p in ctx.Products do
select p }) do
where (p.UnitPrice.Value > 100.0M) }
which (as in Tomas's original code) will likely be evaluated by loading all of the products into memory, and doing the selection in memory, whereas what we really want is something like:
query { for p in ctx.Products do
where (p.UnitPrice.Value > 100.0M) }
which will turn into an SQL selection query. FSharpComposableQuery
overrides the QueryBuilder
to perform this, among other, transformations. So, queries can be composed using quotation and antiquotation more freely.
The project home page is here: http://fsprojects.github.io/FSharp.Linq.ComposableQuery/
and there is some more discussion in an answer I just provided to another (old) question about dynamic queries: How do you compose query expressions in F#?
Comments or questions (especially if something breaks or something that you think should work doesn't) are very welcome.
[EDIT: Updated the links to the project pages, which have just been changed to remove the word "Experimental".]
In F# 3.0, the query is quoted automatically and so you cannot use quotation splicing (the <@ foo %bar @>
syntax) that makes composing queries possible. Most of the things that you could write by composing queries using splicing can still be done in the "usual LINQ way" by creating a new query from the previous source and adding i.e. filtering:
// Initial query that simply selects products
let q1 =
query { for p in ctx.Products do
select p }
// Create a new query that specifies only expensive products
let q2 =
query { for p in q1 do
where (p.UnitPrice.Value > 100.0M) }
This way, you can dynamically add conditions, dynamically specify projection (using select
) and do a couple of other query compositions. However, you don't get the full flexibility of composing queries as with explicit quotations. I guess this is the price that F# 3.0 has to pay for a simpler syntax similar to what exists in C#.
In principle, you should be able to write query explicitly using the query.Select
(etc.) operators. This would be written using explicit quotations and so you should be able to use splicing. However, I don't exactly know how the translation works, so I can't give you a working sample. Something like this should work (but the syntax is very ugly, so it is probably better to just use strings or some other techniques):
<@ query.Select(Linq.QuerySource<_, _>(ctx.Products), fun prod ->
// You could use splicing here, for example, if 'projection' is
// a quotation that specifies the projection, you could write:
// %projection
prod.ProductName) @>
|> query.Run
The queries in F# 3.0 are based on IQueryable
, so it might be possible to use the same trick as the one that I implemented for C#. However, I guess that some details would be different, so I wouldn't expect that to work straight away. The best implementation of that idea is in LINQKit, but I think it won't directly work in F#.
So, in general, I think the only case that works well is the first example - where you just apply additional query operators to the query by writing multiple queries.