MySQL Provider
SQLProvider with MySQL is available via multiple NuGet-Packages:
- NuGet: SQLProvider.MySql - Fixed libraries. Doesn't need resolution path. TypeProvider class:
FSharp.Data.Sql.MySql.SqlDataProvider - NuGet: SQLProvider.MySqlConnector - Fixed libraries. Works also with MariaDB. Doesn't need resolution path. TypeProvider class:
FSharp.Data.Sql.MySqlConnector.SqlDataProvider - NuGet: SQLProvider - Dynamic version. TypeProvider class:
FSharp.Data.Sql.SqlDataProvider
Parameters
ConnectionString
A basic connection string used to connect to MySQL instance; typical connection string parameters apply here. See MySQL Connector/NET Connection Strings Documentation for a complete list of connection string options.
[<Literal>]
let connString = "Server=localhost;Database=HR;User=root;Password=password"
To deal with some MySQL data connection problems, you might want to add some more parameters to connectionstring:
Auto Enlist=false; Convert Zero Datetime=true;
ConnectionStringName
Instead of storing the connection string in the source code, you
can store it in the App.config file. This is the name of the
connectionString key/value pair stored in App.config (TODO: confirm filename).
// found in App.config (TODO: confirm)
let connexStringName = "DefaultConnectionString"
Database Vendor
Use MYSQL from the FSharp.Data.Sql.Common.DatabaseProviderTypes enumeration.
[<Literal>]
let dbVendor = Common.DatabaseProviderTypes.MYSQL
Resolution Path
Path to search for assemblies containing database vendor-specific connections and custom types. Type the path where
Mysql.Data.dll is stored. Both absolute and relative paths are supported.
[<Literal>]
let resPath = __SOURCE_DIRECTORY__ + @"/../../../packages/tests/MySql.Data/lib/net45"
Individuals Amount
Sets the count of records to load for each table. See individuals for further info.
[<Literal>]
let indivAmount = 1000
Use Option Types
If true, F# option types will be used in place of nullable database columns. If false, you will always receive the default value of the column's type even if it is null in the database.
[<Literal>]
let useOptTypes = FSharp.Data.Sql.Common.NullableColumnType.OPTION
Example
type sql = SqlDataProvider<
dbVendor,
connString,
ResolutionPath = resPath,
IndividualsAmount = indivAmount,
UseOptionTypes = useOptTypes,
Owner = "HR"
>
let ctx = sql.GetDataContext()
let employees =
ctx.Hr.Employees
|> Seq.map (fun e -> e.ColumnValues |> Seq.toList)
|> Seq.toList
Working with Type-mappings
Basic types
MySql.Data types are not always the ones you have used to in .NET, so here is a little help:
let myEmp =
query {
for jh in ctx.Hr.JobHistory do
where (jh.Years > 10u)
select (jh)
} |> Seq.head
let myUint32 = 10u
let myInt64 = 10L
let myUInt64 = 10UL
System.Guid Serialization
If you use a string column to save a Guid to the database, you may want to skip the hyphens ("-") when serializing them:
let myGuid = System.Guid.NewGuid() //e.g. b8fa7880-ce44-4315-8d60-a160e5734c4b
let myGuidAsString = myGuid.ToString("N") // e.g. "b8fa7880ce4443158d60a160e5734c4b"
The problem with this is that you should never forget to use "N" anywhere.
System.DateTime Serialization
Another problem with MySql.Data is that DateTime conversions may fail if your culture is not the expected one.
So you may have to convert datetimes as strings instead of using just myEmp.BirthDate <- DateTime.UtcNow:
myEmp.SetColumn("BirthDate", DateTime.UtcNow.ToString("yyyy-MM-dd HH\:mm\:ss") |> box)
Notice that if you use .ToString("s"), there will be "T" between date and time: "yyyy-MM-ddTHH\:mm\:ss".
And comparing two datetimes as strings with "T" and without "T" will generate a problem with the time-part.
If your DateTime columns are strings in the database, you can use DateTime.Parse in your where-queries:
let longAgoeTime.UtcNow.AddYears(-5)
let myEmp =
query {
for emp in ctx.Hr.Employees do
where (DateTime.Parse(emp.HireDate) > longAgo)
select (emp)
} |> Seq.head
You should be fine even with canonical functions like DateTime.Parse(a.MeetStartTime).AddMinutes(10.).
Caveats / Additional Info
Check General, Static Parameters and Querying documentation.
Support for MySqlConnector
MySqlConnector is an alternative driver to use instead of MySql.Data.dll. It has fewer features but a lot better performance than the official driver.
You can use it with SQLProvider: Just remove MySql.Data.dll from your resolutionPath and insert there MySqlConnector.dll instead. (Get the latest from NuGet.) It uses references to System.Buffers.dll, System.Runtime.InteropServices.RuntimeInformation.dll and System.Threading.Tasks.Extensions.dll, so copy those files also to your referencePath. You can get them from corresponding NuGet packages.
If you want to use the drivers in parallel, you need two resolution paths:
type HRFast = SqlDataProvider<Common.DatabaseProviderTypes.MYSQL, connString, ResolutionPath = @"c:\mysqlConnectorPath", Owner = "HR">
type HRProcs = SqlDataProvider<Common.DatabaseProviderTypes.MYSQL, connString, ResolutionPath = @"creferencesOwner = "HR">
(**
### Example performance difference from our unit tests
One complex query:
```
MySql.Data.dll: Real: 00:00:00.583, C0:00:00.484, GC gen0: 1, gen1: 0, gen2: 0
MySqlConnector.dll: Real: 00:00:00.173, CPU: 00:00:00.093, GC gen0: 1, gen1: 0, gen2: 0
```
Lots of async queries:
```
MySQL.Data.dll Real: 00:00:01.425, CPU: 00:00:02.078, GC gen0: 16, gen1: 1, gen2: 0
MySqlConnector.dll: Real: 00:00:01.091, CPU: 00:00:02.000, GC gen0: 14, gen1: 1, gen2: 0
```
*)
namespace FSharp
--------------------
namespace Microsoft.FSharp
namespace FSharp.Data
--------------------
namespace Microsoft.FSharp.Data
type LiteralAttribute = inherit Attribute new: unit -> LiteralAttribute
--------------------
new: unit -> LiteralAttribute
<summary> Specifies the database provider type for the SQL type provider. Each provider has its own specific implementation for SQL generation and data type mapping. </summary>
<summary> MySQL using MySql.Data or MySqlConnector provider </summary>
<summary> Specifies how to represent nullable database columns in the generated types. </summary>
<summary> Option types are Option<_>. (Old true.) </summary>
module Seq from FSharp.Data.Sql
--------------------
module Seq from Microsoft.FSharp.Collections
[<Struct>] type Guid = new: b: byte array -> unit + 6 overloads member CompareTo: value: Guid -> int + 1 overload member Equals: g: Guid -> bool + 1 overload member GetHashCode: unit -> int member ToByteArray: unit -> byte array + 1 overload member ToString: unit -> string + 2 overloads member TryFormat: utf8Destination: Span<byte> * bytesWritten: byref<int> * ?format: ReadOnlySpan<char> -> bool + 1 overload member TryWriteBytes: destination: Span<byte> -> bool + 1 overload static member (<) : left: Guid * right: Guid -> bool static member (<=) : left: Guid * right: Guid -> bool ...
<summary>Represents a globally unique identifier (GUID).</summary>
--------------------
Guid ()
Guid(b: byte array) : Guid
Guid(b: ReadOnlySpan<byte>) : Guid
Guid(g: string) : Guid
Guid(b: ReadOnlySpan<byte>, bigEndian: bool) : Guid
Guid(a: int, b: int16, c: int16, d: byte array) : Guid
Guid(a: int, b: int16, c: int16, d: byte, e: byte, f: byte, g: byte, h: byte, i: byte, j: byte, k: byte) : Guid
Guid(a: uint32, b: uint16, c: uint16, d: byte, e: byte, f: byte, g: byte, h: byte, i: byte, j: byte, k: byte) : Guid
Guid.ToString(format: string) : string
Guid.ToString(format: string, provider: IFormatProvider) : string
[<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)
<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>
DateTime.ToString(format: string) : string
DateTime.ToString(provider: IFormatProvider) : string
DateTime.ToString(format: string, provider: IFormatProvider) : string