Adding a Mapper using dataContext to use generated types from the DB
Typically, F# is about writing business logic and not about OR-mapping. Consider using your database types as is. And select only the columns you need, not full entities. But sometimes you want to map objects to different ones, for example to interact with other languages like C# domain.
First, add a Domain Model
open System
type Employee = {
EmployeeId : int64
FirstName : string
LastName : string
HireDate : DateTime
}
Then you can create the mapper using dataContext to use the generated types from the DB
let mapEmployee (dbRecord:sql.dataContext.``main.EmployeesEntity``) : Employee =
{ EmployeeId = dbRecord.EmployeeId
FirstName = dbRecord.FirstName
LastName = dbRecord.LastName
HireDate = dbRecord.HireDate }
This could be useful if you e.g. want to use SQLProvider objects in some reflection based code-generator (because the normal objects are erased).
MapTo
SqlProvider also has a .MapTo<'T> convenience method:
let ctx = sql.GetDataContext()
let orders = ctx.Main.Orders
let employees = ctx.Main.Employees
type Employee2 = {
FirstName:string
LastName:string
}
let qry = query { for row in employees do
select row} |> Seq.map (fun x -> x.MapTo<Employee2>())
The target type can be a record (as in the example) or a class type with properties named as the source columns and with a parameterless setter.
Target will support mapping database nullable fields to Option and ValueOption types automatically.
The target field name can also be different than the column name; in this case, it must be decorated with the MappedColumnAttribute custom attribute:
open FSharp.Data.Sql.Common
type Employee3 = {
[<MappedColumn("FirstName")>] GivenName:string
[<MappedColumn("LastName")>] FamilyName:string
}
let qry2 =
query {
for row in employees do
select row} |> Seq.map (fun x -> x.MapTo<Employee3>())
TemplateAsRecord
If you want to use SQLProvider as code-generator and copy and paste the tables as separate classes (not recommended!), you can use TemplateAsRecord under your database table type which is located under dataContext types:
sql.dataContext.DesignTimeCommands.TemplateAsRecord.``main.OrdersTemplate``
// intellisense will generate you code that you can copy and paste as template to create your own type:
// ``type MainOrders = { CustomerId : String voption; EmployeeId : Int64 voption; Freight : Decimal voption; OrderDate : DateTime voption; OrderId : Int64; RequiredDate : DateTime voption; ShipAddress : String voption; ShipCity : String voption; ShipCountry : String voption; ShipName : String voption; ShipPostalCode : String voption; ShipRegion : String voption; ShippedDate : DateTime voption }``
The main reason to do this would be to create some reflection schema or MapTo object templates without manual typing.
ColumnValues
Or alternatively, the ColumnValues from SQLEntity can be used to create a map, with the column as a key:
let rows =
query {
for row in employees do
select row} |> Seq.toArray
let employees2map = rows |> Seq.map(fun i -> i.ColumnValues |> Map.ofSeq)
let firstNames = employees2map |> Seq.map (fun x -> x.["FirstName"])
type LiteralAttribute = inherit Attribute new: unit -> LiteralAttribute
--------------------
new: unit -> LiteralAttribute
namespace FSharp
--------------------
namespace Microsoft.FSharp
namespace FSharp.Data
--------------------
namespace Microsoft.FSharp.Data
<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> SQLite database using System.Data.SQLite or Microsoft.Data.Sqlite </summary>
<summary> Specifies which SQLite library to use for connections. Different libraries may have different capabilities and platform support. </summary>
<summary> .NET Framework default </summary>
<summary> Specifies how to handle case sensitivity when generating table and column names. </summary>
<summary> Keep original casing from the database </summary>
val int64: value: 'T -> int64 (requires member op_Explicit)
--------------------
type int64 = Int64
--------------------
type int64<'Measure> = int64
val string: value: 'T -> string
--------------------
type string = 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)
module Seq from FSharp.Data.Sql
--------------------
module Seq from Microsoft.FSharp.Collections
type MappedColumnAttribute = inherit Attribute new: name: string -> MappedColumnAttribute member Name: string
<summary> Attribute for mapping entity properties to database columns with different names. Use this when your database column name differs from your preferred F# property name. </summary>
--------------------
new: name: string -> MappedColumnAttribute
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>