FSharpComposableQuery


FSharpComposableQuery

The FSharpComposableQuery library can be installed from NuGet:
PM> Install-Package FSharpComposableQuery
F# Project

Overview

The FSharpComposableQuery library exposes a new query builder in place of the old F# one.

All existing F# database and in-memory queries should work as normal. In addition, queries can be composed using lambda-abstraction and are explicitly transformed to a normal form before execution.

See the paper "A Practical Theory of Language-Integrated Query" (ICFP 2013) for further information.

Simple forms of query composition, such as parameterizing a value of base type, already work in LINQ, but 'FSharpComposableQuery' additionally supports passing functions as parameters to allow for higher-order query operations.

The library also performs normalisation on the input query, which allows us to provide strong guarantees about the runtime of composed queries. It thus allows the user to use higher-order abstraction and dynamically construct queries without having to worry about the efficiency of the generated SQL code.

Installation

You can install the library from NuGet. Alternatively you can find the source code on GitHub and build it yourself.

To then include the library in your project or script simply open the FSharpComposableQuery namespace:

1: 
2: 
3: 
4: 
5: 
#if INTERACTIVE     // reference required libraries if run as a script
#r "FSharpComposableQuery.dll"
#endif

open FSharpComposableQuery

Example

The following example demonstrates query composition using a parameterized predicate.

We assume the following simple database schema:

type dbSchema = { people : { name : string; age : int } list; }

We will use the LINQ-to-SQL TypeProvider to connect to the database and get the record types:

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
#if INTERACTIVE
#r "FSharp.Data.TypeProviders.dll"
#r "System.Data.Linq.dll"
#endif

open System
open Microsoft.FSharp.Data.TypeProviders

type dbSchema = SqlDataConnection<ConnectionStringName="PeopleConnectionString", ConfigFile="db.config">

type internal Person = dbSchema.ServiceTypes.People

let db = dbSchema.GetDataContext()

We can then construct a function satisfies that takes a predicate on ages and returns all people that satisfy it

1: 
2: 
3: 
4: 
5: 
6: 
let satisfies  = 
 <@ fun p -> query { 
    for u in db.People do
        if p u.Age then 
            yield p
   } @>

We can now use this function, for example, to find all people with age at least 20 and less than 30:

1: 
let ex1 = query { yield! (%satisfies) (fun x -> 20 <= x && x < 30 ) }

Find all people with an even age:

1: 
let ex2 = query { yield! (%satisfies) (fun x ->  x % 2 = 0 ) }

An overview of the main use cases of the library can be found here

Caveats

  • WARNING: F# compiler optimizations are no longer applied to in-memory queries. This library should only be used for database query programming. If both in-memory querying and composable database queries are needed, you can explicitly bind the FSharpComposableQuery builder to a variable instead of opening the whole namespace to avoid shadowing the built-in query keyword:
1: 
let dbQuery =  FSharpComposableQuery.TopLevelValues.query
  • Please check the issues page on GitHub for further information.

Samples & documentation

The API reference is automatically generated from Markdown comments in the library implementation.

  • The tutorial contains a further overview of this library's main use cases.

  • Query Examples contains a comprehensive set of default queries from the MSDN documentation.

  • API Reference contains automatically generated documentation for all types, modules and functions in the library. This includes additional brief samples on using most of the functions.

Contributing and copyright

The project is hosted on GitHub where you can report issues, fork the project and submit pull requests. If you're adding new public API, please also consider adding samples that can be turned into a documentation. You might also want to read library design notes to understand how it works.

The library is available under MIT license, which allows modification and redistribution for both commercial and non-commercial purposes. For more information see the License file in the GitHub repository.

namespace FSharpComposableQuery
namespace System
namespace Microsoft
namespace Microsoft.FSharp
namespace Microsoft.FSharp.Data
namespace Microsoft.FSharp.Data.TypeProviders
type dbSchema

Full name: Index.dbSchema
type SqlDataConnection

Full name: Microsoft.FSharp.Data.TypeProviders.SqlDataConnection


<summary>Provides the types to access a database, using a LINQ-to-SQL mapping</summary><param name='ConnectionString'>The connection string for the database connection. If using Visual Studio, a connection string can be found in database properties in the Server Explorer window.</param><param name='ConnectionStringName'>The name of the connection string for the database connection in the configuration file.</param><param name='LocalSchemaFile'>The local .dbml file for the database schema (default: no local schema file)</param><param name='ForceUpdate'>Require that a direct connection to the database be available at design-time and force the refresh of the local schema file (default: true)</param><param name='Pluralize'>Automatically pluralize or singularize class and member names using English language rules (default: false)</param><param name='Views'>Extract database views (default: true)</param><param name='Functions'>Extract database functions (default: true)</param><param name='ConfigFile'>The name of the configuration file used for connection strings (default: app.config or web.config is used)</param><param name='DataDirectory'>The name of the data directory, used to replace |DataDirectory| in connection strings (default: the project or script directory)</param><param name='ResolutionFolder'>The folder used to resolve relative file paths at compile-time (default: folder containing the project or script)</param><param name='StoredProcedures'>Extract stored procedures (default: true)</param><param name='Timeout'>Timeout value in seconds to use when SqlMetal accesses the database (default: 0, which means infinite)</param><param name='ContextTypeName'>The name of data context class (default: derived from database name)</param><param name='Serializable'>Generate uni-directional serializable classes (default: false, which means no serialization)</param>
type internal Person = obj

Full name: Index.Person
val db : obj

Full name: Index.db
val satisfies : Quotations.Expr<((int -> bool) -> Linq.IQueryable<(int -> bool)>)>

Full name: Index.satisfies
val p : (int -> bool)
val query : QueryImpl.QueryBuilder

Full name: FSharpComposableQuery.TopLevelValues.query
val u : obj
val ex1 : Linq.IQueryable<(int -> bool)>

Full name: Index.ex1
val x : int
val ex2 : Linq.IQueryable<(int -> bool)>

Full name: Index.ex2
val dbQuery : QueryImpl.QueryBuilder

Full name: Index.dbQuery
module TopLevelValues

from FSharpComposableQuery
Fork me on GitHub