-
Notifications
You must be signed in to change notification settings - Fork 20
/
Db.fs
164 lines (139 loc) · 6.87 KB
/
Db.fs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
namespace Donald
open System
open System.Data
open System.Data.Common
open System.Threading.Tasks
open System.Threading
#if NETSTANDARD2_0 || NETSTANDARD2_1
open FSharp.Control.Tasks
#endif
[<RequireQualifiedAccess>]
module Db =
/// Create a new DbUnit instance using the provided IDbConnection.
let newCommand (commandText : string) (conn : IDbConnection) =
let cmd = conn.CreateCommand()
cmd.CommandText <- commandText
DbUnit(cmd)
/// Configure the CancellationToken for the provided DbUnit
let setCancellationToken (cancellationToken : CancellationToken) (dbunit : DbUnit) =
dbunit.CancellationToken <- cancellationToken
dbunit
/// Configure the CommandBehavior for the provided DbUnit
let setCommandBehavior (commandBehavior : CommandBehavior) (dbUnit : DbUnit) =
dbUnit.CommandBehavior <- commandBehavior
dbUnit
/// Configure the CommandType for the provided DbUnit
let setCommandType (commandType : CommandType) (dbUnit : DbUnit) =
dbUnit.Command.CommandType <- commandType
dbUnit
/// Configure the command parameters for the provided DbUnit
let setParams (param : RawDbParams) (dbUnit : DbUnit) =
dbUnit.Command.SetDbParams(DbParams.create param) |> ignore
dbUnit
/// Configure the timeout for the provided DbUnit
let setTimeout (commandTimeout : int) (dbUnit : DbUnit) =
dbUnit.Command.CommandTimeout <- commandTimeout
dbUnit
/// Configure the transaction for the provided DbUnit
let setTransaction (tran : IDbTransaction) (dbUnit : DbUnit) =
dbUnit.Command.Transaction <- tran
dbUnit
let private tryDo (fn : IDbCommand -> 'a) (cmd : IDbCommand) : Result<'a, DbError> =
try
cmd.Connection.TryOpenConnection() |> ignore
let result = fn cmd
cmd.Dispose()
Ok result
with
| DbFailureException e -> Error e
/// Execute parameterized query with no results.
let exec (dbUnit : DbUnit) : Result<unit, DbError> =
tryDo (fun dbUnit -> dbUnit.Exec()) dbUnit.Command
/// Execute parameterized query many times with no results.
let execMany (param : RawDbParams list) (dbUnit : DbUnit) : Result<unit, DbError> =
try
for p in param do
let dbParams = DbParams.create p
dbUnit.Command.SetDbParams(dbParams).Exec() |> ignore
Ok ()
with
| DbFailureException e -> Error e
/// Execute scalar query and box the result.
let scalar (convert : obj -> 'a) (dbUnit : DbUnit) : Result<'a, DbError> =
tryDo (fun cmd ->
let value = cmd.ExecuteScalar()
convert value)
dbUnit.Command
/// Execute parameterized query, enumerate all records and apply mapping.
let query (map : 'reader -> 'a when 'reader :> IDataReader) (dbUnit : DbUnit) : Result<'a list, DbError> =
tryDo (fun cmd ->
use rd = cmd.ExecReader(dbUnit.CommandBehavior) :?> 'reader
[ while rd.Read() do yield map rd ])
dbUnit.Command
/// Execute paramterized query, read only first record and apply mapping.
let querySingle (map : 'reader -> 'a when 'reader :> IDataReader) (dbUnit : DbUnit) : Result<'a option, DbError> =
tryDo (fun cmd ->
use rd = cmd.ExecReader(dbUnit.CommandBehavior) :?> 'reader
if rd.Read() then Some(map rd) else None)
dbUnit.Command
/// Execute paramterized query and return IDataReader
let read (dbUnit : DbUnit) : IDataReader =
dbUnit.Command.ExecReader(dbUnit.CommandBehavior)
module Async =
let private tryDoAsync (cancellationToken : CancellationToken) (fn : DbCommand -> Task<'a>) (cmd : IDbCommand) : Task<Result<'a, DbError>> =
task {
try
do! cmd.Connection.TryOpenConnectionAsync(cancellationToken)
let! result = fn (cmd :?> DbCommand)
return (Ok result)
with
| DbFailureException e -> return Error e
}
/// Asynchronously execute parameterized query with no results.
let exec (dbUnit : DbUnit) : Task<Result<unit, DbError>> =
let inner = fun (cmd : DbCommand) -> task {
let! _ = cmd.ExecAsync(dbUnit.CancellationToken)
return ()
}
tryDoAsync dbUnit.CancellationToken inner dbUnit.Command
/// Asynchronously execute parameterized query many times with no results
let execMany (param : RawDbParams list) (dbUnit : DbUnit) : Task<Result<unit, DbError>> =
let inner = fun (cmd : DbCommand) -> task {
for p in param do
let dbParams = DbParams.create p
let! _ = cmd.SetDbParams(dbParams).ExecAsync(dbUnit.CancellationToken)
()
return ()
}
tryDoAsync dbUnit.CancellationToken inner dbUnit.Command
/// Execute scalar query and box the result.
let scalar (convert : obj -> 'a) (dbUnit : DbUnit) : Task<Result<'a, DbError>> =
let inner = fun (cmd : DbCommand) -> task {
let! value = cmd.ExecuteScalarAsync(dbUnit.CancellationToken)
return convert value
}
tryDoAsync dbUnit.CancellationToken inner dbUnit.Command
/// Asynchronously execute parameterized query, enumerate all records and apply mapping.
let query (map : 'reader -> 'a when 'reader :> IDataReader) (dbUnit : DbUnit) : Task<Result<'a list, DbError>> =
let inner = fun (cmd : IDbCommand) -> task {
use! rd = (cmd :?> DbCommand).ExecReaderAsync(dbUnit.CommandBehavior, dbUnit.CancellationToken)
let rd' = rd :?> 'reader
return [ while rd.Read() do map rd' ]
}
tryDoAsync dbUnit.CancellationToken inner dbUnit.Command
/// Asynchronously execute paramterized query, read only first record and apply mapping.
let querySingle (map : 'reader -> 'a when 'reader :> IDataReader) (dbUnit : DbUnit) : Task<Result<'a option, DbError>> =
let inner = fun (cmd : DbCommand) -> task {
use! rd = cmd.ExecReaderAsync(dbUnit.CommandBehavior, dbUnit.CancellationToken)
let rd' = rd :?> 'reader
return if rd.Read() then Some(map rd') else None
}
tryDoAsync dbUnit.CancellationToken inner dbUnit.Command
/// Asynchronously execute paramterized query and return IDataReader
let read (dbUnit : DbUnit) : Task<IDataReader> =
let cmd' = dbUnit.Command :?> DbCommand
task {
let! rd = cmd'.ExecReaderAsync(dbUnit.CommandBehavior, dbUnit.CancellationToken)
let result = rd :> IDataReader
return result
}