FSharpComposableQuery
PM> Install-Package FSharpComposableQuery
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.
Full name: Index.dbSchema
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>
Full name: Index.Person
Full name: Index.db
Full name: Index.satisfies
Full name: FSharpComposableQuery.TopLevelValues.query
Full name: Index.ex1
Full name: Index.ex2
Full name: Index.dbQuery
from FSharpComposableQuery