SQLProvider

How to see the SQL-clause?

To display / debug your SQL-clauses you can add listener for your logging framework to SqlQueryEvent:

FSharp.Data.Sql.Common.QueryEvents.SqlQueryEvent |> Event.add (printfn "Executing SQL: %O")

The event has separate fields of Command and Parameters for you to store your clauses with a strongly typed logging system like Logary.

Querying

type sql  = SqlDataProvider<
                Common.DatabaseProviderTypes.SQLITE,
                connectionString,
                SQLiteLibrary=Common.SQLiteLibrary.SystemDataSQLite,
                ResolutionPath = resolutionPath,
                CaseSensitivityChange = Common.CaseSensitivityChange.ORIGINAL
            >
let ctx = sql.GetDataContext()

SQLProvider leverages F#'s query {} expression syntax to perform queries against the database. Though many are supported, not all LINQ expressions are.

let example =
    query {
        for order in ctx.Main.Orders do
        where (order.Freight > 0m)
        sortBy (order.ShipPostalCode)
        skip 3
        take 4
        select (order)
    }

let test = example |> Seq.toArray |> Array.map(fun i -> i.ColumnValues |> Map.ofSeq)

let item =
    query {
        for order in ctx.Main.Orders do
        where (order.Freight > 0m)
        head
    }

Or async versions:

let exampleAsync =
    task {
        let! res =
            query {
                for order in ctx.Main.Orders do
                where (order.Freight > 0m)
                select (order)
            } |> Seq.executeQueryAsync
        return res
    } 

let itemAsync =
    task {
        let! item =
            query {
                for order in ctx.Main.Orders do
                where (order.Freight > 0m)
            } |> Seq.headAsync
        return item
    } 

If you consider using asynchronous queries, read more from the async documentation.

SELECT -clause operations

You can control the execution context of the select-operations by GetDataContext parameter selectOperations. The LINQ-query stays the same. You have two options: DotNetSide or DatabaseSide.

This might have a significant effect on the size of data transferred from the database.

SelectOperations.DotNetSide (Default)

Fetch the columns and run operations in .NET-side.

    let dc = sql.GetDataContext(SelectOperations.DotNetSide) // (same as without the parameter)
    query {
        for cust in dc.Main.Customers do
        select (if cust.Country = "UK" then (cust.City)
            else ("Outside UK"))
    } |> Seq.toArray
SELECT 
   [cust].[Country] as 'Country',
   [cust].[City] as 'City' 
FROM main.Customers as [cust]

SelectOperations.DatabaseSide

Execute the operations as part of SQL.

let dc = sql.GetDataContext(SelectOperations.DatabaseSide)
let qry = 
    query {
        for cust in dc.Main.Customers do
        select (if cust.Country = "UK" then (cust.City)
            else ("Outside UK"))
    } |> Seq.toArray
SELECT 
   CASE WHEN ([cust].[Country] = @param1) THEN 
   [cust].[City] 
   ELSE @param2 
END as [result] 
FROM main.Customers as [cust]
-- params @param1 - "UK"; @param2 - "Outside UK"

Supported Query Expression Keywords

Keyword

Supported

Notes

.Contains()

X

open System.Linq, in where, SQL IN-clause, nested query

.Concat()

X

open System.Linq, SQL UNION ALL-clause

.Union()

X

open System.Linq, SQL UNION-clause

all

X

averageBy

X

Single table (1)

averageByNullable

X

Single table (1)

contains

X

count

X

distinct

X

exactlyOne

X

exactlyOneOrDefault

X

exists

X

find

X

groupBy

x

Simple support (2)

groupJoin

groupValBy

head

X

headOrDefault

X

if

X

join

X

last

lastOrDefault

leftOuterJoin

let

x

...but not using tmp variables in where-clauses

maxBy

X

Single table (1)

maxByNullable

X

Single table (1)

minBy

X

Single table (1)

minByNullable

X

Single table (1)

nth

X

select

X

skip

X

skipWhile

sortBy

X

sortByDescending

X

sortByNullable

X

sortByNullableDescending

X

sumBy

X

Single table (1)

sumByNullable

X

Single table (1)

take

X

takeWhile

thenBy

X

thenByDescending

X

thenByNullable

X

thenByNullableDescending

X

where

x

Server side variables must either be plain without .NET operations or use the supported canonical functions.

Currently SQL-provider doesn't generate nested queries in from-clauses, the query is flattened to a single select. Nested in-clauses in where-clauses are supported. (1) Single table, if you want multiple tables, use corresponding Seq query or async aggregates, like Seq.sumQuery or Seq.sumAsync. (2) Very simple groupBy (and having) is supported: Single table, or max 3 table joins before groupBy, with direct aggregates like .Count() or direct parameter calls like .Sum(fun entity -> entity.UnitPrice), and max 7 key columns. No nested grouping.

Canonical Functions

Besides that, we support these .NET-functions to transfer the logics to SQL-clauses (starting from SQLProvider version 1.0.57). If you use these, remember to check your database indexes.

.NET String Functions (.NET)

.NET

MsSqlServer

PostgreSql

MySql

Oracle

SQLite

MSAccess

Odbc

Notes

.Substring(x)

SUBSTRING

SUBSTRING

MID

SUBSTR

SUBSTR

Mid

SUBSTRING

Start position may vary (0 or 1 based.)

.ToUpper()

UPPER

UPPER

UPPER

UPPER

UPPER

UCase

UCASE

.ToLower()

LOWER

LOWER

LOWER

LOWER

LOWER

LCase

LCASE

.Trim()

LTRIM(RTRIM)

TRIM(BOTH...)

TRIM

TRIM

TRIM

Trim

LTRIM(RTRIM)

.Length()

DATALENGTH

CHAR_LENGTH

CHAR_LENGTH

LENGTH

LENGTH

Len

CHARACTER_LENGTH

.Replace(a,b)

REPLACE

REPLACE

REPLACE

REPLACE

REPLACE

Replace

REPLACE

.IndexOf(x)

CHARINDEX

STRPOS

LOCATE

INSTR

INSTR

InStr

LOCATE

.IndexOf(x, i)

CHARINDEX

LOCATE

INSTR

InStr

LOCATE

(+)

+

||

CONCAT

||

||

&

CONCAT

In where-clauses you can also use .Contains("..."), .StartsWith("...") and .EndsWith("..."), which are translated to corresponding LIKE-clauses (e.g. StartsWith("abc") is LIKE ('asdf%').

Substring(startpos,length) is supported. IndexOf with length paramter is supported except PostgreSql and SQLite.

Operations do support parameters to be either constants or other SQL-columns (e.g. x.Substring(x.Length() - 1)).

.NET DateTime Functions

.NET

MsSqlServer

PostgreSql

MySql

Oracle

SQLite

MSAccess

Odbc

Notes

.Date

CAST(AS DATE)

DATE_TRUNC

DATE

TRUNC

STRFTIME

DateValue(Format)

CONVERT(SQL_DATE)

.Year

YEAR

DATE_PART

YEAR

EXTRACT

STRFTIME

Year

YEAR

.Month

MONTH

DATE_PART

MONTH

EXTRACT

STRFTIME

Month

MONTH

.Day

DAY

DATE_PART

DAY

EXTRACT

STRFTIME

Day

DAYOFMONTH

.Hour

DATEPART HOUR

DATE_PART

HOUR

EXTRACT

STRFTIME

Hour

HOUR

.Minute

DATEPART MINUTE

DATE_PART

MINUTE

EXTRACT

STRFTIME

Minute

MINUTE

.Second

DATEPART SECOND

DATE_PART

SECOND

EXTRACT

STRFTIME

Second

SECOND

.Subtract(y).Days

DATEDIFF

y-x

DATEDIFF

y-x

x-y

DateDiff

DATEDIFF

.Subtract(y).Seconds

TIMESTAMPDIFF

EXTRACT(EPOCH)

TIMESTAMPDIFF

y-x

x-y

DateDiff

DATEDIFF

.AddYears(i)

DATEADD YEAR

  • INTERVAL

DATE_ADD

  • INTERVAL

DATETIME

DateAdd

.AddMonths(i)

DATEADD MONTH

  • INTERVAL

DATE_ADD

  • INTERVAL

DATETIME

DateAdd

.AddDays(f)

DATEADD DAY

  • INTERVAL

DATE_ADD

  • INTERVAL

DATETIME

DateAdd

.AddHours(f)

DATEADD HOUR

  • INTERVAL

DATE_ADD

  • INTERVAL

DATETIME

DateAdd

.AddMinutes(f)

DATEADD MINUTE

  • INTERVAL

DATE_ADD

  • INTERVAL

DATETIME

DateAdd

.AddSeconds(f)

DATEADD SECOND

  • INTERVAL

DATE_ADD

  • INTERVAL

DATETIME

DateAdd

AddYears, AddDays and AddMinutes parameter can be either constant or other SQL-column, except in SQLite which supports only constant. AddMonths, AddHours and AddSeconds supports only constants for now. Odbc standard doesn't seem to have a date-add functionality. .NET has float parameters on some time-functions like AddDays, but SQL may ignore the decimal fraction.

Numerical Functions (e.g. Microsoft.FSharp.Core.Operators)

.NET

MsSqlServer

PostgreSql

MySql

Oracle

SQLite

MSAccess

Odbc

Notes

abs(i)

ABS

ABS

ABS

ABS

ABS

Abs

ABS

ceil(i)

CEILING

CEILING

CEILING

CEIL

CAST + 0.5

Fix+1

CEILING

floor(i)

FLOOR

FLOOR

FLOOR

FLOOR

CAST AS INT

Int

FLOOR

round(i)

ROUND

ROUND

ROUND

ROUND

ROUND

Round

ROUND

Math.Round(i,x)

ROUND

ROUND

ROUND

ROUND

ROUND

Round

ROUND

truncate(i)

TRUNCATE

TRUNC

TRUNCATE

TRUNC

Fix

TRUNCATE

sqrt(i)

SQRT

SQRT

SQRT

SQRT

SQRT

Sqr

SQRT

sin(i)

SIN

SIN

SIN

SIN

SIN

SIN

SIN

cos(i)

COS

COS

COS

COS

COS

COS

COS

tan(i)

TAN

TAN

TAN

TAN

TAN

TAN

TAN

asin(i)

ASIN

ASIN

ASIN

ASIN

ASIN

ASIN

acos(i)

ACOS

ACOS

ACOS

ACOS

ACOS

ACOS

atan(i)

ATAN

ATAN

ATAN

ATAN

ATAN

Atn

ATAN

Math.Max(x,y)

SELECT(MAX)

GREATEST

GREATEST

GREATEST

MAX

iif(x>y,x,y)

GREATEST

Math.Min(x,y)

SELECT(MIN)

LEAST

LEAST

LEAST

MIN

iif(x<y,x,y)

LEAST

Math.Pow(x,y)

POWER(x,y)

POWER(x,y)

POWER(x,y)

POWER(x,y)

x^y

POWER(x,y)

(+)

+

+

+

+

+

+

+

(-)

-

-

-

-

-

-

-

(*)

*

*

*

*

*

*

*

(/)

/

/

/

/

/

/

/

(%)

%

%

%

%

%

%

%

Microsoft SQL Server doesn't have Greatest and Least functions, so that will be done via nested SQL clause: (select max(v) from (values (x), (y)) as value(v)) It might also not be standard ODBC, but should work e.g. on Amazon Redshift.

Condition operations and others

.NET

MsSqlServer

PostgreSql

MySql

Oracle

SQLite

MSAccess

Odbc

Notes

.ToString()

CAST(NVARCHAR)

::varchar

CAST(CHAR)

CAST(VARCHAR)

CAST(TEXT)

CStr

CONVERT

if x then y else z

CASE WHEN

CASE WHEN

IF(x,y,z)

CASE WHEN

CASE WHEN

iif(x,y,z)

CASE WHEN

If the condition is not using SQL columns, it will be parsed before creation of SQL. If the condition is containing columns, it will be parsed into SQL.

If the condition is in the result of projection (the final select clause), it may be parsed after execution of the SQL, depending on parameter setting selectOperations.

Aggregate Functions

Also you can use these to return an aggregated value, or in a group-by clause:

.NET

MsSqlServer

PostgreSql

MySql

Oracle

SQLite

MSAccess

Odbc

Notes

count

COUNT

COUNT

COUNT

COUNT

COUNT

COUNT

COUNT

sum

SUM

SUM

SUM

SUM

SUM

SUM

SUM

min

MIN

MIN

MIN

MIN

MIN

MIN

MIN

max

MAX

MAX

MAX

MAX

MAX

MAX

MAX

average

AVG

AVG

AVG

AVG

AVG

AVG

AVG

StdDev

STDEV

STDDEV

STDDEV

STDDEV

STDEV

STDEV

Variance

VAR

VARIANCE

VARIANCE

VARIANCE

DVAR

VAR

StdDev, Variance are located in FSharp.Data.Sql.Operators namespace and also Seq.stdDevAsync and Seq.varianceAsync. Others can be used from List, Seq and Array modules, or Seq.countAsync, Seq.sumAsync, Seq.minAsync, Seq.maxAsync, Seq.averageAsync.

More details

By default query { ... } is IQueryable<T> which is lazy. To execute the query you have to do Seq.toList, Seq.toArray, or some corresponding operation. If you don't do that but just continue inside another query { ... } or use System.Linq .Where(...) etc, that will still be combined to the same SQL-query.

There are some limitation of complexity of your queries, but for example this is still ok and will give you very simple select-clause:

let randomBoolean = 
    let r = System.Random()
    fun () -> r.NextDouble() > 0.5
let c1 = randomBoolean()
let c2 = randomBoolean()
let c3 = randomBoolean()

let sample =
    query {
        for order in ctx.Main.Orders do
        where ((c1 || order.Freight > 0m) && c2)
        let x = "Region: " + order.ShipAddress
        select (x, if c3 then order.ShipCountry else order.ShipRegion)
    } |> Seq.toArray

It can be for example (but it can also leave [Freight]-condition away and select ShipRegion instead of ShipAddress, depending on your randon values):

    SELECT 
        [_arg2].[ShipAddress] as 'ShipAddress',
        [_arg2].[ShipCountry] as 'ShipCountry' 
    FROM main.Orders as [_arg2] 
    WHERE (([_arg2].[Freight]> @param1)) 

Expressions

These operators perform no specific function in the code itself, rather they are placeholders replaced by their database-specific server-side operations. Their utility is in forcing the compiler to check against the correct types.

let bergs = ctx.Main.Customers.Individuals.BERGS

Operators

You can find some custom operators using FSharp.Data.Sql:

Best practices working with queries

When using Option types, check IsSome in where-clauses.

You may want to use F# Option to represent database null, with SQLProvider static constructor parameter UseOptionTypes = true. Database null-checking is done with x IS NULL. With option types, easiest way to do that is to check IsSome and IsNone:

let result =
    query {
        for order in ctx.Main.Orders do
        where (
            order.ShippedDate.IsSome && 
            order.ShippedDate.Value.Year = 2015)
        select (order.OrderId, order.Freight)
    } |> Array.executeQueryAsync

Using booleans and simple variables (from outside a scope) in where-clauses

This is how you make your code easier to read when you have multiple code paths. SQLProvider will optimize the SQL-clause before sending it to the database, so it will still be simple.

Consider how clean is this source-code compared to other with similar logic:

open System.Linq
let getOrders(futureOrders:bool, shipYears:int list) =

    let today = DateTime.UtcNow.Date
    let pastOrders = not futureOrders
    let noYearFilter = shipYears.IsEmpty

    let result =
        query {
            for order in ctx.Main.Orders do
            where ( 
                (noYearFilter || shipYears.Contains(order.ShippedDate.Year))
                &&
                ((futureOrders && order.OrderDate > today) ||
                 (pastOrders   && order.OrderDate <= today))
            ) 
            select (order.OrderId, order.Freight)
        } |> Array.executeQueryAsync
    result

Don't select all the fields if you don't need them

In general you should select only columns you need and not a whole object if you don't update its fields.

// Select all the fields from a table, basically:
// SELECT TOP 1 Address, City, CompanyName, 
//      ContactName, ContactTitle, Country, 
//      CustomerID, Fax, Phone, PostalCode, 
//      Region FROM main.Customers
let selectFullObject =
    query {
        for customer in ctx.Main.Customers do
        select customer
    } |> Seq.tryHeadAsync

// Select only two fields, basically:
// SELECT TOP 1 Address, City FROM main.Customers
let selectSmallObject =
    query {
        for customer in ctx.Main.Customers do
        select (customer.Address, customer.City)
    } |> Seq.tryHeadAsync

If you still want the whole objects and return those to a client as untyped records, you can use ColumnValues |> Map.ofSeq:

let someQuery =
    query {
        for customer in ctx.Main.Customers do
        //where(...)
        select customer
    } |> Seq.toArray

someQuery |> Array.map(fun c -> c.ColumnValues |> Map.ofSeq)

F# Map values are accessed like this: myItem.["City"]

Using code logic in select-clause

Don't be scared to insert non-Sql syntax to select-clauses. They will be parsed business-logic side!

let fetchOrders customerZone =
    let currentDate = DateTime.UtcNow.Date
    query {
        for order in ctx.Main.Orders do
        // where(...)
        select {
            OrderId = order.OrderId
            Timezone = 
                parseTimezoneFunction(order.ShipRegion, order.ShippedDate, customerZone);
            Status = 
                if order.ShippedDate > currentDate then "Shipped"
                elif order.OrderDate > currentDate then "Ordered"
                elif order.RequiredDate > currentDate then "Late"
                else "Waiting"
            OrderRows = [||];
        }
    } |> Seq.toArray

You can't have a let inside a select, but you can have custom function calls like parseTimezoneFunction here. Just be careful, they are executed for each result item separately. So if you want also SQL to execute there, it's rather better to do a separate function taking a collection as parameter. See below.

Using one sub-query to populate items

Sometimes you want to fetch efficiently sub-items, like "Give me all orders with their order-rows"

In the previous example we fetched OrderRows as empty array. Now we populate those with one query in immutable way:

let orders = fetchOrders 123

let orderIds = 
    orders 
    |> Array.map(fun o -> o.OrderId) 
    |> Array.distinct
    
// Fetch all rows with one query
let subItems =
    query {
        for row in ctx.Main.OrderDetails do
        where (orderIds.Contains(row.OrderId))
        select (row.OrderId, row.ProductId, row.Quantity)
    } |> Seq.toArray
    
let ordersWithDetails =
    orders 
    |> Array.map(fun order ->
        {order with 
            // Match the corresponding sub items
            // to a parent item's colleciton:
            OrderRows = 
                subItems 
                |> Array.filter(fun (orderId,_,_) -> 
                    order.OrderId = orderId)
                })

How to deal with large IN-queries?

The pervious query had orderIds.Contains(row.OrderId). Which is fine if your collection has 50 items but what if there are 5000 orderIds? SQL-IN will fail. You have two easy options to deal with that.

Chunk your collection:

F# has built-in chunkBySize function!

let chunked = orderIds |> Array.chunkBySize 100

for chunk in chunked do
    let all =
        query {
            for row in ctx.Main.OrderDetails do
            where (chunk.Contains(row.OrderId))
            select (row)
        } |> Seq.toArray

    all |> Array.iter(fun row -> row.Discount <- 0.1)
    ctx.SubmitUpdates()

Creating a nested query

By leaving the last |> Seq.toArray away from your main query you create a lazy IQueryable<...>-query. Which means your IN-objects are not fetched from the database, but is actually a nested query.

let nestedOrders =
    query {
        for order in ctx.Main.Orders do
        // where(...)
        select (order.OrderId)
    } 

let subItemsAll =
    query {
        for row in ctx.Main.OrderDetails do
        where (nestedOrders.Contains(row.OrderId))
        select (row.OrderId, row.ProductId, row.Quantity)
    } |> Seq.toArray

// similar as previous fetchOrders
let fetchOrders2 customerZone =
    let currentDate = DateTime.UtcNow.Date
    query {
        for order in ctx.Main.Orders do
        // where(...)
        select {
            OrderId = order.OrderId
            Timezone = 
                parseTimezoneFunction(order.ShipRegion, order.ShippedDate, customerZone);
            Status = 
                if order.ShippedDate > currentDate then "Shipped"
                elif order.OrderDate > currentDate then "Ordered"
                elif order.RequiredDate > currentDate then "Late"
                else "Waiting"
            OrderRows = 
                subItemsAll |> (Array.filter(fun (orderId,_,_) -> 
                    order.OrderId = orderId));
        }
    } |> Seq.toArray

That way order hit count doesn't matter as the database is taking care of it.

Group-by and more complex query scenarios

One problem with SQLProvider is that monitorin the SQL-clause performance hitting to database indexes is hard to track. So the best way to handle complex SQL is to create a database view and query that from SQLProvider.

Still, if you want to use LINQ groupBy, this is how it's done:

let freightsByCity =
    query {
        for o in ctx.Main.Orders do
        //where (...)
        groupBy o.ShipCity into cites
        select (cites.Key, cites.Sum(fun order -> order.Freight))
    } |> Array.executeQueryAsync

Group-by is support is limited, mostly for single tables only. F# Linq query syntax doesnt support doing select count(1), sum(UnitPrice) from Products but you can group by a constant to get that:

let qry = 
    query {
        for p in ctx.Main.Products do
        groupBy 1 into g
        select (g.Count(), g.Sum(fun p -> p.UnitPrice))
    } |> Seq.head

For more info see:

Multiple items
type LiteralAttribute = inherit Attribute new: unit -> LiteralAttribute

--------------------
new: unit -> LiteralAttribute
[<Literal>] val resolutionPath: string = "C:\git\SQLProvider\docs\content\core/../../files/sqlite"
[<Literal>] val connectionString: string = "Data Source=C:\git\SQLProvider\docs\content\core\..\northwindEF.db;Version=3;Read Only=false;FailIfMissing=True;"
Multiple items
namespace FSharp

--------------------
namespace Microsoft.FSharp
Multiple items
namespace FSharp.Data

--------------------
namespace Microsoft.FSharp.Data
namespace FSharp.Data.Sql
namespace System
type CustomOrder = { OrderId: int64 Status: string OrderRows: (int64 * int64 * int16) array Timezone: string }
Multiple items
val int64: value: 'T -> int64 (requires member op_Explicit)

--------------------
type int64 = Int64

--------------------
type int64<'Measure> = int64
Multiple items
val string: value: 'T -> string

--------------------
type string = String
Multiple items
val int16: value: 'T -> int16 (requires member op_Explicit)

--------------------
type int16 = Int16

--------------------
type int16<'Measure> = int16
val parseTimezoneFunction: region: string * sdate: DateTime * customer: int -> string
val region: string
val sdate: DateTime
Multiple items
[<Struct>] type DateTime = new: year: int * month: int * day: int -> unit + 10 overloads member Add: value: TimeSpan -> DateTime member AddDays: value: float -> DateTime member AddHours: value: float -> DateTime member AddMilliseconds: value: float -> DateTime member AddMinutes: value: float -> DateTime member AddMonths: months: int -> DateTime member AddSeconds: value: float -> DateTime member AddTicks: value: int64 -> DateTime member AddYears: value: int -> DateTime ...
<summary>Represents an instant in time, typically expressed as a date and time of day.</summary>

--------------------
DateTime ()
   (+0 other overloads)
DateTime(ticks: int64) : DateTime
   (+0 other overloads)
DateTime(ticks: int64, kind: DateTimeKind) : DateTime
   (+0 other overloads)
DateTime(year: int, month: int, day: int) : DateTime
   (+0 other overloads)
DateTime(year: int, month: int, day: int, calendar: Globalization.Calendar) : DateTime
   (+0 other overloads)
DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int) : DateTime
   (+0 other overloads)
DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int, kind: DateTimeKind) : DateTime
   (+0 other overloads)
DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int, calendar: Globalization.Calendar) : DateTime
   (+0 other overloads)
DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int, millisecond: int) : DateTime
   (+0 other overloads)
DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int, millisecond: int, kind: DateTimeKind) : DateTime
   (+0 other overloads)
val customer: int
Multiple items
val int: value: 'T -> int (requires member op_Explicit)

--------------------
type int = int32

--------------------
type int<'Measure> = int
namespace FSharp.Data.Sql.Common
module QueryEvents from FSharp.Data.Sql.Common
val SqlQueryEvent: IEvent<Common.QueryEvents.SqlEventData>
<summary> This event fires immediately before the execution of every generated query. Listen to this event to display or debug the content of your queries. </summary>
Multiple items
module Event from Microsoft.FSharp.Control

--------------------
type Event<'T> = new: unit -> Event<'T> member Trigger: arg: 'T -> unit member Publish: IEvent<'T>

--------------------
type Event<'Delegate,'Args (requires delegate and 'Delegate :> Delegate and reference type)> = new: unit -> Event<'Delegate,'Args> member Trigger: sender: obj * args: 'Args -> unit member Publish: IEvent<'Delegate,'Args>

--------------------
new: unit -> Event<'T>

--------------------
new: unit -> Event<'Delegate,'Args>
val add: callback: ('T -> unit) -> sourceEvent: IEvent<'Del,'T> -> unit (requires delegate and 'Del :> Delegate)
val printfn: format: Printf.TextWriterFormat<'T> -> 'T
type sql = SqlDataProvider<...>
type SqlDataProvider
<summary>Typed representation of a database</summary> <param name='ConnectionString'>The connection string for the SQL database</param> <param name='ConnectionStringName'>The connection string name to select from a configuration file</param> <param name='DatabaseVendor'> The target database vendor</param> <param name='IndividualsAmount'>The amount of sample entities to project into the type system for each SQL entity type. Default 50. Note GDPR/PII regulations if using individuals with ContextSchemaPath.</param> <param name='UseOptionTypes'>If set, F# option types will be used in place of nullable database columns. If not, you will always receive the default value of the column's type even if it is null in the database.</param> <param name='ResolutionPath'>The location to look for dynamically loaded assemblies containing database vendor specific connections and custom types. Types used in desing-time: If no better clue, prefer .NET Standard 2.0 versions. Semicolon to separate multiple.</param> <param name='Owner'>Oracle: The owner of the schema for this provider to resolve. PostgreSQL: A list of schemas to resolve, separated by spaces, newlines, commas, or semicolons.</param> <param name='CaseSensitivityChange'>Should we do ToUpper or ToLower when generating table names?</param> <param name='TableNames'>Comma separated table names list to limit a number of tables in big instances. The names can have '%' sign to handle it as in the 'LIKE' query (Oracle and MSSQL Only)</param> <param name='ContextSchemaPath'>The location of the context schema previously saved with SaveContextSchema. When not empty, will be used to populate the database schema instead of retrieving it from then database.</param> <param name='OdbcQuote'>Odbc quote characters: Quote characters for the table and column names: `alias`, [alias]</param> <param name='SQLiteLibrary'>Use System.Data.SQLite or Mono.Data.SQLite or select automatically (SQLite only)</param> <param name='SsdtPath'>A path to an SSDT .dacpac file.'</param>
[<Struct>] type DatabaseProviderTypes = | MSSQLSERVER = 0 | SQLITE = 1 | POSTGRESQL = 2 | MYSQL = 3 | ORACLE = 4 | MSACCESS = 5 | ODBC = 6 | FIREBIRD = 7 | MSSQLSERVER_DYNAMIC = 8 | MSSQLSERVER_SSDT = 9
Common.DatabaseProviderTypes.SQLITE: Common.DatabaseProviderTypes = 1
[<Struct>] type SQLiteLibrary = | SystemDataSQLite = 0 | MonoDataSQLite = 1 | AutoSelect = 2 | MicrosoftDataSqlite = 3
Common.SQLiteLibrary.SystemDataSQLite: Common.SQLiteLibrary = 0
[<Struct>] type CaseSensitivityChange = | ORIGINAL = 0 | TOUPPER = 1 | TOLOWER = 2
Common.CaseSensitivityChange.ORIGINAL: Common.CaseSensitivityChange = 0
val ctx: SqlDataProvider<...>.dataContext
SqlDataProvider<...>.GetDataContext() : SqlDataProvider<...>.dataContext
<summary>Returns an instance of the SQL Provider using the static parameters</summary>
   (+0 other overloads)
SqlDataProvider<...>.GetDataContext(selectOperations: SelectOperations) : SqlDataProvider<...>.dataContext
<summary>Returns an instance of the SQL Provider using the static parameters</summary><param name='selectOperations'>Execute select-clause operations in SQL database rather than .NET-side.</param>
   (+0 other overloads)
SqlDataProvider<...>.GetDataContext(commandTimeout: int) : SqlDataProvider<...>.dataContext
<summary>Returns an instance of the SQL Provider using the static parameters</summary><param name='commandTimeout'>SQL command timeout. Maximum time for single SQL-command in seconds.</param>
   (+0 other overloads)
SqlDataProvider<...>.GetDataContext(transactionOptions: Transactions.TransactionOptions) : SqlDataProvider<...>.dataContext
<summary>Returns an instance of the SQL Provider using the static parameters</summary><param name='transactionOptions'>TransactionOptions for the transaction created on SubmitChanges.</param>
   (+0 other overloads)
SqlDataProvider<...>.GetDataContext(connectionString: string) : SqlDataProvider<...>.dataContext
<summary>Returns an instance of the SQL Provider using the static parameters</summary><param name='connectionString'>The database runtime connection string</param>
   (+0 other overloads)
SqlDataProvider<...>.GetDataContext(connectionString: string, selectOperations: SelectOperations) : SqlDataProvider<...>.dataContext
<summary>Returns an instance of the SQL Provider using the static parameters</summary><param name='connectionString'>The database runtime connection string</param><param name='selectOperations'>Execute select-clause operations in SQL database rather than .NET-side.</param>
   (+0 other overloads)
SqlDataProvider<...>.GetDataContext(transactionOptions: Transactions.TransactionOptions, commandTimeout: int) : SqlDataProvider<...>.dataContext
<summary>Returns an instance of the SQL Provider using the static parameters</summary><param name='transactionOptions'>TransactionOptions for the transaction created on SubmitChanges.</param><param name='commandTimeout'>SQL command timeout. Maximum time for single SQL-command in seconds.</param>
   (+0 other overloads)
SqlDataProvider<...>.GetDataContext(connectionString: string, commandTimeout: int) : SqlDataProvider<...>.dataContext
<summary>Returns an instance of the SQL Provider using the static parameters</summary><param name='connectionString'>The database runtime connection string</param><param name='commandTimeout'>SQL command timeout. Maximum time for single SQL-command in seconds.</param>
   (+0 other overloads)
SqlDataProvider<...>.GetDataContext(connectionString: string, transactionOptions: Transactions.TransactionOptions) : SqlDataProvider<...>.dataContext
<summary>Returns an instance of the SQL Provider using the static parameters</summary><param name='connectionString'>The database runtime connection string</param><param name='transactionOptions'>TransactionOptions for the transaction created on SubmitChanges.</param>
   (+0 other overloads)
SqlDataProvider<...>.GetDataContext(connectionString: string, resolutionPath: string) : SqlDataProvider<...>.dataContext
<summary>Returns an instance of the SQL Provider using the static parameters</summary><param name='connectionString'>The database runtime connection string</param><param name='resolutionPath'>The location to look for dynamically loaded assemblies containing database vendor specific connections and custom types. Types used in desing-time: If no better clue, prefer .NET Standard 2.0 versions. Semicolon to separate multiple.</param>
   (+0 other overloads)
val example: Linq.IQueryable<SqlDataProvider<...>.dataContext.main.OrdersEntity>
val query: Linq.QueryBuilder
val order: SqlDataProvider<...>.dataContext.main.OrdersEntity
property SqlDataProvider<...>.dataContext.Main: SqlDataProvider<...>.dataContext.mainSchema with get
property SqlDataProvider<...>.dataContext.mainSchema.Orders: SqlDataProvider<...>.dataContext.mainSchema.main.Orders with get
<summary> The table Orders belonging to schema main</summary>
custom operation: where (bool) Calls Linq.QueryBuilder.Where
property SqlDataProvider<...>.dataContext.main.OrdersEntity.Freight: decimal with get, set
<summary>Freight: money</summary>
custom operation: sortBy ('Key) Calls Linq.QueryBuilder.SortBy
property SqlDataProvider<...>.dataContext.main.OrdersEntity.ShipPostalCode: string with get, set
<summary>ShipPostalCode: nvarchar(10)</summary>
custom operation: skip (int) Calls Linq.QueryBuilder.Skip
custom operation: take (int) Calls Linq.QueryBuilder.Take
custom operation: select ('Result) Calls Linq.QueryBuilder.Select
val test: Map<string,obj> array
Multiple items
module Seq from FSharp.Data.Sql

--------------------
module Seq from Microsoft.FSharp.Collections
val toArray: source: 'T seq -> 'T array
type Array = interface ICollection interface IEnumerable interface IList interface IStructuralComparable interface IStructuralEquatable interface ICloneable member Clone: unit -> obj member CopyTo: array: Array * index: int -> unit + 1 overload member GetEnumerator: unit -> IEnumerator member GetLength: dimension: int -> int ...
<summary>Provides methods for creating, manipulating, searching, and sorting arrays, thereby serving as the base class for all arrays in the common language runtime.</summary>
val map: mapping: ('T -> 'U) -> array: 'T array -> 'U array
val i: SqlDataProvider<...>.dataContext.main.OrdersEntity
property Common.SqlEntity.ColumnValues: (string * obj) seq with get
Multiple items
module Map from Microsoft.FSharp.Collections

--------------------
type Map<'Key,'Value (requires comparison)> = interface IReadOnlyDictionary<'Key,'Value> interface IReadOnlyCollection<KeyValuePair<'Key,'Value>> interface IEnumerable interface IStructuralEquatable interface IComparable interface IEnumerable<KeyValuePair<'Key,'Value>> interface ICollection<KeyValuePair<'Key,'Value>> interface IDictionary<'Key,'Value> new: elements: ('Key * 'Value) seq -> Map<'Key,'Value> member Add: key: 'Key * value: 'Value -> Map<'Key,'Value> ...

--------------------
new: elements: ('Key * 'Value) seq -> Map<'Key,'Value>
val ofSeq: elements: ('Key * 'T) seq -> Map<'Key,'T> (requires comparison)
val item: SqlDataProvider<...>.dataContext.main.OrdersEntity
custom operation: head Calls Linq.QueryBuilder.Head
val exampleAsync: Threading.Tasks.Task<SqlDataProvider<...>.dataContext.main.OrdersEntity seq>
val task: TaskBuilder
val res: SqlDataProvider<...>.dataContext.main.OrdersEntity seq
val executeQueryAsync: (Linq.IQueryable<'a> -> Threading.Tasks.Task<'a seq>)
<summary> Execute SQLProvider query and release the OS thread while query is being executed. </summary>
val itemAsync: Threading.Tasks.Task<SqlDataProvider<...>.dataContext.main.OrdersEntity>
val headAsync: (Linq.IQueryable<'a> -> Threading.Tasks.Task<'a>)
<summary> Execute SQLProvider query to take one result and release the OS thread while query is being executed. Like normal head: Throws exception if no elements exists. See also tryHeadAsync. </summary>
val dc: obj
val cust: obj
module Seq from Microsoft.FSharp.Collections
val randomBoolean: (unit -> bool)
val r: Random
Multiple items
type Random = new: unit -> unit + 1 overload member Next: unit -> int + 2 overloads member NextBytes: buffer: byte array -> unit + 1 overload member NextDouble: unit -> float member NextInt64: unit -> int64 + 2 overloads member NextSingle: unit -> float32 static member Shared: Random
<summary>Represents a pseudo-random number generator, which is an algorithm that produces a sequence of numbers that meet certain statistical requirements for randomness.</summary>

--------------------
Random() : Random
Random(Seed: int) : Random
Random.NextDouble() : float
val c1: bool
val c2: bool
val c3: bool
val sample: (string * string) array
val x: string
property SqlDataProvider<...>.dataContext.main.OrdersEntity.ShipAddress: string with get, set
<summary>ShipAddress: nvarchar(60)</summary>
property SqlDataProvider<...>.dataContext.main.OrdersEntity.ShipCountry: string with get, set
<summary>ShipCountry: nvarchar(15)</summary>
property SqlDataProvider<...>.dataContext.main.OrdersEntity.ShipRegion: string with get, set
<summary>ShipRegion: nvarchar(15)</summary>
val bergs: SqlDataProvider<...>.dataContext.main.CustomersEntity
property SqlDataProvider<...>.dataContext.mainSchema.Customers: SqlDataProvider<...>.dataContext.mainSchema.main.Customers with get
<summary> The table Customers belonging to schema main</summary>
property SqlDataProvider<...>.dataContext.mainSchema.main.Customers.Individuals: SqlDataProvider<...>.dataContext.main.Customers.Individuals with get
<summary>Get individual items from the table. Requires single primary key.</summary>
property SqlDataProvider<...>.dataContext.main.Customers.Individuals.BERGS: SqlDataProvider<...>.dataContext.main.CustomersEntity with get
module Array from Microsoft.FSharp.Collections
namespace System.Linq
val getOrders: futureOrders: bool * shipYears: int list -> Threading.Tasks.Task<(int64 * decimal) array>
val futureOrders: bool
type bool = Boolean
val shipYears: int list
type 'T list = List<'T>
val today: DateTime
property DateTime.UtcNow: DateTime with get
<summary>Gets a <see cref="T:System.DateTime" /> object that is set to the current date and time on this computer, expressed as the Coordinated Universal Time (UTC).</summary>
<returns>An object whose value is the current UTC date and time.</returns>
property DateTime.Date: DateTime with get
<summary>Gets the date component of this instance.</summary>
<returns>A new object with the same date as this instance, and the time value set to 12:00:00 midnight (00:00:00).</returns>
val pastOrders: bool
val noYearFilter: bool
property List.IsEmpty: bool with get
val result: Threading.Tasks.Task<(int64 * decimal) array>
(extension) Collections.Generic.IEnumerable.Contains<'TSource>(value: 'TSource) : bool
(extension) Collections.Generic.IEnumerable.Contains<'TSource>(value: 'TSource, comparer: Collections.Generic.IEqualityComparer<'TSource>) : bool
property SqlDataProvider<...>.dataContext.main.OrdersEntity.ShippedDate: DateTime with get, set
<summary>ShippedDate: datetime</summary>
property DateTime.Year: int with get
<summary>Gets the year component of the date represented by this instance.</summary>
<returns>The year, between 1 and 9999.</returns>
property SqlDataProvider<...>.dataContext.main.OrdersEntity.OrderDate: DateTime with get, set
<summary>OrderDate: datetime</summary>
property SqlDataProvider<...>.dataContext.main.OrdersEntity.OrderId: int64 with get, set
<summary>OrderID: integer</summary>
val executeQueryAsync: query: IQueryable<'a> -> Threading.Tasks.Task<'a array>
<summary> Execute SQLProvider query and release the OS thread while query is being executed. </summary>
val selectFullObject: Threading.Tasks.Task<SqlDataProvider<...>.dataContext.main.CustomersEntity option>
val customer: SqlDataProvider<...>.dataContext.main.CustomersEntity
val tryHeadAsync: (IQueryable<'a> -> Threading.Tasks.Task<'a option>)
<summary> Execute SQLProvider query to take one result and release the OS thread while query is being executed. Returns None if no elements exists. </summary>
val selectSmallObject: Threading.Tasks.Task<(string * string) option>
property SqlDataProvider<...>.dataContext.main.CustomersEntity.Address: string with get, set
<summary>Address: nvarchar(60)</summary>
property SqlDataProvider<...>.dataContext.main.CustomersEntity.City: string with get, set
<summary>City: nvarchar(15)</summary>
val someQuery: SqlDataProvider<...>.dataContext.main.CustomersEntity array
val c: SqlDataProvider<...>.dataContext.main.CustomersEntity
val fetchOrders: customerZone: int -> CustomOrder array
val customerZone: int
val currentDate: DateTime
property SqlDataProvider<...>.dataContext.main.OrdersEntity.RequiredDate: DateTime with get, set
<summary>RequiredDate: datetime</summary>
val orders: CustomOrder array
val orderIds: int64 array
val o: CustomOrder
CustomOrder.OrderId: int64
val distinct: array: 'T array -> 'T array (requires equality)
val subItems: (int64 * int64 * int16) array
val row: CustomOrder
property SqlDataProvider<...>.dataContext.mainSchema.OrderDetails: SqlDataProvider<...>.dataContext.mainSchema.main.OrderDetails with get
<summary> The table OrderDetails belonging to schema main</summary>
val ordersWithDetails: CustomOrder array
val order: CustomOrder
val filter: predicate: ('T -> bool) -> array: 'T array -> 'T array
val orderId: int64
val chunked: int64 array array
val chunkBySize: chunkSize: int -> array: 'T array -> 'T array array
val chunk: int64 array
val all: CustomOrder array
val iter: action: ('T -> unit) -> array: 'T array -> unit
SqlDataProvider<...>.dataContext.SubmitUpdates() : Unit
<summary>Save changes to data-source. May throws errors: To deal with non-saved items use GetUpdates() and ClearUpdates().</summary>
val nestedOrders: IQueryable<int64>
val subItemsAll: (int64 * int64 * int16) array
val row: SqlDataProvider<...>.dataContext.main.OrderDetailsEntity
(extension) Collections.Generic.IEnumerable.Contains<'TSource>(value: 'TSource) : bool
(extension) IQueryable.Contains<'TSource>(item: 'TSource) : bool
(extension) Collections.Generic.IEnumerable.Contains<'TSource>(value: 'TSource, comparer: Collections.Generic.IEqualityComparer<'TSource>) : bool
(extension) IQueryable.Contains<'TSource>(item: 'TSource, comparer: Collections.Generic.IEqualityComparer<'TSource>) : bool
val fetchOrders2: customerZone: int -> CustomOrder array
val freightsByCity: Threading.Tasks.Task<(string * decimal) array>
val o: SqlDataProvider<...>.dataContext.main.OrdersEntity
custom operation: groupBy ('Key) Calls Linq.QueryBuilder.GroupBy
property SqlDataProvider<...>.dataContext.main.OrdersEntity.ShipCity: string with get, set
<summary>ShipCity: nvarchar(15)</summary>
val cites: IGrouping<string,SqlDataProvider<...>.dataContext.main.OrdersEntity>
property IGrouping.Key: string with get
(extension) Collections.Generic.IEnumerable.Sum<'TSource>(selector: Func<'TSource,decimal>) : decimal
(extension) Collections.Generic.IEnumerable.Sum<'TSource>(selector: Func<'TSource,float>) : float
(extension) Collections.Generic.IEnumerable.Sum<'TSource>(selector: Func<'TSource,int>) : int
(extension) Collections.Generic.IEnumerable.Sum<'TSource>(selector: Func<'TSource,int64>) : int64
(extension) Collections.Generic.IEnumerable.Sum<'TSource>(selector: Func<'TSource,Nullable<decimal>>) : Nullable<decimal>
(extension) Collections.Generic.IEnumerable.Sum<'TSource>(selector: Func<'TSource,Nullable<float>>) : Nullable<float>
(extension) Collections.Generic.IEnumerable.Sum<'TSource>(selector: Func<'TSource,Nullable<int>>) : Nullable<int>
(extension) Collections.Generic.IEnumerable.Sum<'TSource>(selector: Func<'TSource,Nullable<int64>>) : Nullable<int64>
(extension) Collections.Generic.IEnumerable.Sum<'TSource>(selector: Func<'TSource,Nullable<float32>>) : Nullable<float32>
(extension) Collections.Generic.IEnumerable.Sum<'TSource>(selector: Func<'TSource,float32>) : float32
val qry: int * obj
val p: obj
property SqlDataProvider<...>.dataContext.mainSchema.Products: SqlDataProvider<...>.dataContext.mainSchema.main.Products with get
<summary> The table Products belonging to schema main</summary>
val g: IGrouping<int,obj>
(extension) Collections.Generic.IEnumerable.Count<'TSource>() : int
(extension) Collections.Generic.IEnumerable.Count<'TSource>(predicate: Func<'TSource,bool>) : int
val head: source: 'T seq -> 'T

Type something to start searching.