Skip to content

Commit e428e11

Browse files
committed
More tweaks for correctness and consistency.
1 parent 51f510f commit e428e11

3 files changed

Lines changed: 15 additions & 15 deletions

File tree

doc/Language/UserTypes/README.md

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ module MyCustomMappings =
119119
```
120120

121121
You can have as many classes as you want defining static custom mappings. But you can't split the mapping for a *single
122-
usertype* across multiple classes. `ToPrimitive : Foo -> string` has to be defined in the *same* class as `FromPrimitive
122+
UserType* across multiple classes. `ToPrimitive : Foo -> string` has to be defined in the *same* class as `FromPrimitive
123123
: string -> Foo` for the mapping to be valid.
124124

125125
## Using the mapped types
@@ -261,7 +261,7 @@ module ExampleOverrides =
261261
let unixEpoch = DateTime(1970,1,1)
262262
type System.DateTime with
263263
member this.ToPrimitive() : int64 = int64 (this - unixEpoch).TotalSeconds
264-
static member FromPrimitive(i : int64) = unixEpoch + TimeSpan.FromSeconds(i)
264+
static member FromPrimitive(i : int64) = unixEpoch + TimeSpan.FromSeconds(float i)
265265
266266
// change Guid to store as a string instead of a byte[] blob
267267
type System.Guid with
@@ -274,20 +274,20 @@ UserTypes, when it's a builtin type you've re-mapped, you *don't* have to be cas
274274
queries. That would just be far too confusing if typing `DateTime` applied your overridden methods but `datetime`
275275
didn't!
276276

277-
### Decimal and DateTimeOffset on SQLite
277+
### Supporting Decimal and DateTimeOffset on SQLite
278278

279-
I especially recommend doing this for the SQLite backend if you'd like to use `decimal` or `DateTimeOffset`. By default
280-
these types will throw an exception if used with a SQLite backend because I couldn't think of an acceptable *default*
281-
way to support them. For decimal, if we mapped to `REAL` you would lose the precision and basetenity of `decimal`. The
282-
only lossless way to store and retrieve a `decimal` value would be in a SQLite `BLOB` or `TEXT` column, but then
283-
mathematical operators would break or silently decay to binary floating point.
279+
Custom mapping can help you with the SQLite backend if you'd like to use `decimal` or `DateTimeOffset`. By default these
280+
types will throw an exception if used with a SQLite backend because I couldn't think of an acceptable *default* way to
281+
support them. For decimal, if we mapped to `REAL` you would lose the precision and base-10 math of `decimal`. The only
282+
lossless way to store and retrieve a `decimal` value would be in a SQLite `BLOB` or `TEXT` column, but then mathematical
283+
operators would break or silently decay to binary floating point.
284284

285285
Likewise with `DateTimeOffset`, the obvious choice would be to use `.ToString("o")` like we do with DateTime, but then
286-
comparisons and equality would produce unexpected results. The below expression evalutes TRUE in SQLite using string
287-
comparison, but should be FALSE comparing the actual moment in time the two `DateTimeOffset` types represent. The UTC+0
286+
comparisons and equality would produce unexpected results. The below expression evaluates FALSE in SQLite using string
287+
comparison, but should be TRUE comparing the actual moment in time two `DateTimeOffset` types represent. The UTC+0
288288
one is a minute before the UTC-4 one.
289289

290-
`'2026-06-07T21:16:00.0000000-04:00' > '2026-06-08T01:15:00.0000000+00:00'`
290+
`'2026-06-08T01:15:00.0000000+00:00' < '2026-06-07T21:16:00.0000000-04:00'`
291291

292292
If you understand the problem space and have chosen storage format where the tradeoffs work for *your needs*, mapping
293293
these types can be the right call.

doc/Language/UserTypes/SolutionLayout.gv

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ digraph "Solution Layout" {
2323
<table border="3" cellborder="1" cellspacing="0" cellpadding="14">
2424
<tr><td><b>YourProject.UserTypes.fsproj</b></td></tr>
2525
<tr><td align="left">type IUser = abstract member Id : UserId...</td></tr>
26-
<tr><td align="left">type UserId = UserId of int</td></tr>
26+
<tr><td align="left">type UserId = UserId of Guid</td></tr>
2727
<tr><td align="left">[&lt;SQLTypeLength(254)&gt;]<br align="left"/>type EmailAddress = EmailAddress of string</td></tr>
2828
<tr><td align="left">type System.TimeOnly with member this.ToPrimitive() = this.ToString(&quot;o&quot;)...</td></tr>
2929
</table>
@@ -35,7 +35,7 @@ digraph "Solution Layout" {
3535
<tr><td>rzsql.json</td><td align="left">{ &quot;UserTypes&quot;: [&quot;YourProject.UserTypes&quot;] }</td></tr>
3636
<tr><td>V1.model.sql</td><td align="left">create table Users(Id UserId primary key, Email EmailAddress, ...)</td></tr>
3737
<tr><td rowspan="3">Query.fs</td><td align="left">type MyQuery = SQL&lt;&quot;select&lt;IUser&gt; * from Users where Id = @id&quot;&gt;</td></tr>
38-
<tr><td align="left">let! rows = MyQuery.Command(id = UserId 123).Plan()</td></tr>
38+
<tr><td align="left">let! rows = MyQuery.Command(id = UserId(myGuid)).Plan()</td></tr>
3939
<tr><td align="left">let user = rows.[0] :&gt; IUser</td></tr>
4040

4141
</table>

doc/Language/UserTypes/SolutionLayout.gv.svg

Lines changed: 2 additions & 2 deletions
Loading

0 commit comments

Comments
 (0)