More on composing queries in F#
I ran across some StackOverflow questions from a couple of years ago asking about composing query expressions in F#, and gave answers showing how FSharpComposableQuery can help with this:
http://stackoverflow.com/questions/13826749/how-do-you-compose-query-expressions-in-f
http://stackoverflow.com/questions/10158512/dynamic-sql-queries-with-f-3-0
Tomas Petricek's previous posts on dynamic LINQ queries in C# and F# 2.0 were, of course part of the inspiration for our work. FSharpComposableQuery updates these ideas to F# 3.0 and, hopefully, makes them easily usable without the need for tricks.
I also ran across a more recent blog post by Loïc Denuziere that discusses the related issue of how to splice partial F# query expressions together to build more complex ones. His approach cleverly defines an alternative query operator, pquery, that can be used to define query snippets that don't get evaluated immediately. He suggests the following code (copied from the end of the post):
// Utility code, write it once
type
PartialQueryBuilder() =
inherit
Linq.QueryBuilder()
member
this.Run(e: Expr<Linq.QuerySource<'T, IQueryable>>) = e
let
pquery = PartialQueryBuilder()
type
Linq.QueryBuilder
with
[<ReflectedDefinition>]
member
this.Source(qs: Linq.QuerySource<'T, _>) = qs
This code means that any queries written using pquery effectively just get wrapped in quotations, and QueryBuilder.Source is overloaded to recognize these quotations and embed them into ordinary queries seamlessly. This can be used as follows (code also copied from the post):
// Example usage
let
GetLatestMessages num usernameOpt =
use
db =
(* retrieve LINQ-to-SQL context *)
let
baseQuery =
pquery{
for
m
in
ctx.Messages
do
sortBy m.PostedDate
select m }
let
filteredQuery =
match
usernameOpt
with
| None -> baseQuery
| Some username ->
pquery{
for
m
in
%baseQuery
do
join u
in
db.Users on (m.UserId = u.Id)
where (u.Username = username)
select m }
query
{
for
m
in
%filteredQuery
do
take num }
|> Array.ofSeq
In FSharpComposableQuery, this is not necessary, because wherever one would write pquery {...} one can instead just write <@ query {...} @> and normalization ensures that the right thing will happen. For example, I believe Loïc's main example program can be written as follows using FSharpComposableQuery:
let
GetLatestMessages num usernameOpt =
use
db =
(* retrieve LINQ-to-SQL context *)
let
baseQuery =
<@ query{
for
m
in
ctx.Messages
do
sortBy m.PostedDate
select m } @>
let
filteredQuery =
match
usernameOpt
with
| None -> baseQuery
| Some username ->
<@ query{
for
m
in
%baseQuery
do
join u
in
db.Users on (m.UserId = u.Id)
where (u.Username = username)
select m } @>
query
{
for
m
in
%filteredQuery
do
take num }
|> Array.ofSeq
This idea seems beneficial (though not strictly necessary) in FSharpComposableQuery also, by decreasing the amount of visual clutter around partial query expressions, so it may make sense to incorporate pquery into the library at some stage, pending testing to make sure that everything still works.
Labels: databases, functional programming, programming languages
0 Comments:
Post a Comment
Subscribe to Post Comments [Atom]
<< Home