I'm want to create a method which would construct an equivalent of query { ... } computation expression (using FSharp.Data.TypeProviders with either LINQ2SQL or LINQ2Entities) from provided data structure, i.e.:
Collection("customers", [
Field "customerId";
Collection("orders", [
Field "orderId" ]) ])
For that I need to understand how the query is translated into code on the first place. Given following query as an example:
query {
for c in db.Customers do
select (c.CustomerID, query {
for o in c.Orders do
select o.OrderID
} |> Seq.toList)
}
What would it look like without computation expression syntax?
I would not recommend generating F# queries programmatically.
The main purpose of queries is to make it possible to write nicely looking F# source code. When they are executed, the code you write is first translated to LINQ-style expression tree, which is then translated to SQL query. If you managed to translate your query specification into F# query, you'd have something like:
+------------+ +----------+ +----------------------+ +-----+
| Your query | -> | F# query | -> | LINQ expression tree | -> | SQL |
+------------+ +----------+ +----------------------+ +-----+
There is nothing wrong with multiple transformations, but there is a number of things that make the path via F# query more complicated:
In your query format, things seem to be represented as strings. So, you'd need to go from strings to .NET MethodInfo just to go back to strings.
With F# queries/LINQ, you get back typed objects and that's one of the main benefits, but if you build query dynamically, you'll just get back a squence of obj values - and will have to use reflection or something like that to access them.
TL;DR - I think it will be much easier to just take your query representation and generate SQL directly. The structure of the transformation is similar to generating F# query, but you won't have to mess around with quotations and reflection.
If you want to see how something desugars, just put it into a quotation. If you do this with your example, you'll see that
<@
query {
for c in db.Customers do
select (c.CustomerID, query {
for o in c.Orders do
select o.OrderID
} |> Seq.toList)
}
@>
is roughly equivalent to
query.Run
<@ query.Select(query.For(query.Source db.Customers,
fun c -> query.Yield c),
fun c ->
c.CustomerID,
query.Run <@
query.Select(query.For(query.Source c.Orders,
fun o -> query.Yield o),
fun o -> o.OrderID) @>
|> Seq.toList) @>
(where I've cleaned up a few extraneous variables). I agree with Tomas that there are probably better ways to achieve what you want than trying to create such an expression directly.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With