How to see the SQL-clause?
To display / debug your SQL-clauses you can add a 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 the GetDataContext
parameter selectOperations
.
The LINQ-query stays the same. You have two options: DotNetSide or DatabaseSide.
This might significantly affect the size of data transferred from the database.
SelectOperations.DotNetSide (Default)
Fetch the columns and run operations on the .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
|
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
|
If your query is a sub-query (a part of a larger query), then the operations are always executed on DatabaseSide.
Supported Query Expression Keywords
Keyword |
Supported |
Notes |
---|---|---|
.Contains() |
X |
|
.Concat() |
X |
|
.Union() |
X |
|
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 parameter 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 |
|
DATE_ADD |
|
DATETIME |
DateAdd |
||
.AddMonths(i) |
DATEADD MONTH |
|
DATE_ADD |
|
DATETIME |
DateAdd |
||
.AddDays(f) |
DATEADD DAY |
|
DATE_ADD |
|
DATETIME |
DateAdd |
||
.AddHours(f) |
DATEADD HOUR |
|
DATE_ADD |
|
DATETIME |
DateAdd |
||
.AddMinutes(f) |
DATEADD MINUTE |
|
DATE_ADD |
|
DATETIME |
DateAdd |
||
.AddSeconds(f) |
DATEADD SECOND |
|
DATE_ADD |
|
DATETIME |
DateAdd |
AddYears, AddDays and AddMinutes parameters can be either constant or other SQL-column, except in SQLite which supports only constant. AddMonths, AddHours and AddSeconds support 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 it 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 does not use SQL columns, it will be parsed before creating SQL. If the condition is containing columns, it will be parsed into SQL.
If the condition is the result of projection (the final select clause),
it may be parsed after execution of the SQL, depending on the 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 perform some corresponding operations. 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 limitations to the complexity of your queries but for example this is still ok and will give you a 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 random values):
|
Expressions
These operators perform no specific function in the code itself. Instead, 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
:
|=|
(In set)|<>|
(Not in set)=%
(Like)<>%
(Not like)!!
(Left join)
Best practices working with queries
When using Option types, check IsSome in where-clauses.
You may want to use the 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, the 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 others 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
Technical details
This is what happens behind the scenes:
The F# query syntax is LINQ Abstract Syntax Tree (AST), and SQLProvider does process those on querying.
Don't select all the fields if you don't need them
In general, you should select only the columns you need and only a whole object if you 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
Feel free 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 separately for each result item.
So, if you also want SQL to execute there, doing a separate function and taking
a collection as a parameter is better. 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 an empty array. Now, we populate those with one query in an 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 previous query had orderIds.Contains(row.OrderId)
.
This 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. This means your IN-objects are not fetched from
the database but are actually formed as 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, the order hit count doesn't matter as the database takes care of it.
Group-by and more complex query scenarios
One problem with SQLProvider is that monitoring the SQL-clause performance hitting 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 support is limited, mostly for single tables only.
F# Linq query syntax doesn't 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:
type LiteralAttribute = inherit Attribute new: unit -> LiteralAttribute
--------------------
new: unit -> LiteralAttribute
namespace FSharp
--------------------
namespace Microsoft.FSharp
namespace FSharp.Data
--------------------
namespace Microsoft.FSharp.Data
val int64: value: 'T -> int64 (requires member op_Explicit)
--------------------
type int64 = Int64
--------------------
type int64<'Measure> = int64
val string: value: 'T -> string
--------------------
type string = String
val int16: value: 'T -> int16 (requires member op_Explicit)
--------------------
type int16 = Int16
--------------------
type int16<'Measure> = int16
[<Struct>] type DateTime = new: date: DateOnly * time: TimeOnly -> unit + 16 overloads member Add: value: TimeSpan -> DateTime member AddDays: value: float -> DateTime member AddHours: value: float -> DateTime member AddMicroseconds: 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 ...
<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(date: DateOnly, time: TimeOnly) : DateTime
(+0 other overloads)
DateTime(ticks: int64, kind: DateTimeKind) : DateTime
(+0 other overloads)
DateTime(date: DateOnly, time: TimeOnly, 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)
val int: value: 'T -> int (requires member op_Explicit)
--------------------
type int = int32
--------------------
type int<'Measure> = int
<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>
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>
<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>
<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)
<summary> The table Orders belonging to schema main</summary>
<summary>Freight: money</summary>
<summary>ShipPostalCode: nvarchar(10)</summary>
module Seq from FSharp.Data.Sql
--------------------
module Seq from Microsoft.FSharp.Collections
<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>
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>
<summary> Execute SQLProvider query and release the OS thread while query is being executed. </summary>
<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>
type Random = new: unit -> unit + 1 overload member GetItems<'T> : choices: ReadOnlySpan<'T> * length: int -> 'T array + 2 overloads 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 member Shuffle<'T> : values: Span<'T> -> unit + 1 overload 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
<summary>ShipAddress: nvarchar(60)</summary>
<summary>ShipCountry: nvarchar(15)</summary>
<summary>ShipRegion: nvarchar(15)</summary>
<summary> The table Customers belonging to schema main</summary>
<summary>Get individual items from the table. Requires single primary key.</summary>
<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>
<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>
(extension) Collections.Generic.IEnumerable.Contains<'TSource>(value: 'TSource, comparer: Collections.Generic.IEqualityComparer<'TSource>) : bool
<summary>ShippedDate: datetime</summary>
<summary>Gets the year component of the date represented by this instance.</summary>
<returns>The year, between 1 and 9999.</returns>
<summary>OrderDate: datetime</summary>
<summary>OrderID: integer</summary>
<summary> Execute SQLProvider query and release the OS thread while query is being executed. </summary>
<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>
<summary>Address: nvarchar(60)</summary>
<summary>City: nvarchar(15)</summary>
<summary>RequiredDate: datetime</summary>
<summary> The table OrderDetails belonging to schema main</summary>
<summary>Save changes to data-source. May throws errors: To deal with non-saved items use GetUpdates() and ClearUpdates().</summary>
(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
<summary>ShipCity: nvarchar(15)</summary>
(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
<summary> The table Products belonging to schema main</summary>
(extension) Collections.Generic.IEnumerable.Count<'TSource>(predicate: Func<'TSource,bool>) : int