From b58a9f9733d51baebd1bfeaea1fe9cd1330f3be2 Mon Sep 17 00:00:00 2001 From: Kerry Perret Date: Wed, 6 Jan 2021 08:18:48 +0100 Subject: [PATCH 1/4] feat(tests): refactoring wip --- Vp.FSharp.Sql.Tests/Helpers.fs | 101 +++++++++ Vp.FSharp.Sql.Tests/Mocks.fs | 26 ++- .../SqlCommand for executeNonQuery should.fs | 115 ++++------- .../SqlCommand for executeScalar should.fs | 119 ++++------- ...lCommand for executeScalarOrNone should.fs | 193 ++++++------------ .../SqlCommand for queryAsyncSeq should.fs | 193 +++++++----------- .../SqlCommand for querySetListN should.fs | 76 +++---- .../Vp.FSharp.Sql.Tests.fsproj | 1 + Vp.FSharp.Sql/Helpers.fs | 29 ++- Vp.FSharp.Sql/SqlCommand.fs | 46 +++-- 10 files changed, 416 insertions(+), 483 deletions(-) create mode 100644 Vp.FSharp.Sql.Tests/Helpers.fs diff --git a/Vp.FSharp.Sql.Tests/Helpers.fs b/Vp.FSharp.Sql.Tests/Helpers.fs new file mode 100644 index 0000000..d7d6bb5 --- /dev/null +++ b/Vp.FSharp.Sql.Tests/Helpers.fs @@ -0,0 +1,101 @@ +module Vp.FSharp.Sql.Tests.Helpers + +open Swensen.Unquote.Assertions + +open Vp.FSharp.Sql + + +type FullCallCounter = + { mutable OpenCall: int32 + mutable CloseCall: int32 + mutable ConnectionOpened: int32 + mutable ConnectionClosed: int32 + mutable CommandPrepared: int32 + mutable CommandExecuted: int32 } + +[] +module FullCallCounter = + + let incrOpenCall (callCounter: FullCallCounter) = + callCounter.OpenCall <- callCounter.OpenCall + 1 + + let incrCloseCall (callCounter: FullCallCounter) = + callCounter.CloseCall <- callCounter.CloseCall + 1 + + let incrConnectionOpened (callCounter: FullCallCounter) = + callCounter.ConnectionOpened <- callCounter.ConnectionOpened + 1 + + let incrConnectionClosed (callCounter: FullCallCounter) = + callCounter.ConnectionClosed <- callCounter.ConnectionClosed + 1 + + let incrCommandPrepared (callCounter: FullCallCounter) = + callCounter.CommandPrepared <- callCounter.CommandPrepared + 1 + + let incrCommandExecuted (callCounter: FullCallCounter) = + callCounter.CommandExecuted <- callCounter.CommandExecuted + 1 + + let createLoggerCallback callCounter = + function + | ConnectionOpened _ -> incrConnectionOpened callCounter + | ConnectionClosed _ -> incrConnectionClosed callCounter + | CommandPrepared _ -> incrCommandPrepared callCounter + | CommandExecuted _ -> incrCommandExecuted callCounter + let createOpenCallback (callCounter: FullCallCounter) = fun() -> incrOpenCall callCounter + let createCloseCallback (callCounter: FullCallCounter) = fun() -> incrCloseCall callCounter + let createCallbacks (callCounter: FullCallCounter) = + (createOpenCallback callCounter, createCloseCallback callCounter, createLoggerCallback callCounter) + + let init openCall closeCall connectionOpened connectionClosed commandPrepared commandExecuted = + { OpenCall = openCall + CloseCall = closeCall + ConnectionOpened = connectionOpened + ConnectionClosed = connectionClosed + CommandPrepared = commandPrepared + CommandExecuted = commandExecuted } + + let initSame value = init value value value value value value + + let assertEqual + (actual: FullCallCounter) + expectedOpenCall + expectedCloseCall + expectedConnectionOpened + expectedConnectionClosed + expectedCommandPrepared + expectedCommandExecuted = + actual.OpenCall =! expectedOpenCall + actual.CloseCall =! expectedCloseCall + actual.ConnectionOpened =! expectedConnectionOpened + actual.ConnectionClosed =! expectedConnectionClosed + actual.CommandPrepared =! expectedCommandPrepared + actual.CommandExecuted =! expectedCommandExecuted + +type PartialCallCounter = + { mutable OpenCall: int32 + mutable CloseCall: int32 } + +[] +module PartialCallCounter = + let incrOpenCall (callCounter: PartialCallCounter) = + callCounter.OpenCall <- callCounter.OpenCall + 1 + + let incrCloseCall (callCounter: PartialCallCounter) = + callCounter.CloseCall <- callCounter.CloseCall + 1 + + let createOpenCallback (callCounter: PartialCallCounter) = fun() -> incrOpenCall callCounter + let createCloseCallback (callCounter: PartialCallCounter) = fun() -> incrCloseCall callCounter + let createCallbacks (callCounter: PartialCallCounter) = + (createOpenCallback callCounter, createCloseCallback callCounter) + + let init openCall closeCall = + { OpenCall = openCall + CloseCall = closeCall } + + let initSame value = init value value + + let assertEqual + (actual: PartialCallCounter) + expectedOpenCall + expectedCloseCall = + actual.OpenCall =! expectedOpenCall + actual.CloseCall =! expectedCloseCall diff --git a/Vp.FSharp.Sql.Tests/Mocks.fs b/Vp.FSharp.Sql.Tests/Mocks.fs index 09807dd..2b532b3 100644 --- a/Vp.FSharp.Sql.Tests/Mocks.fs +++ b/Vp.FSharp.Sql.Tests/Mocks.fs @@ -7,17 +7,17 @@ open System.Data.Common open Vp.FSharp.Sql + type DbField' = { Name: string FieldType: Type NativeTypeName: string } -type Data = { - Columns: DbField' list list - GetValues: int32 -> int32 -> Object list - CountRows: int32 -> int32 - CountResultSets: int32 -} +type Data = + { Columns: DbField' list list + GetValues: int32 -> int32 -> Object list + CountRows: int32 -> int32 + CountResultSets: int32 } type Response = | Reader of (CommandBehavior -> DbDataReader) @@ -41,15 +41,13 @@ let fakeData values columns = | resultSetIndex when resultSetIndex >= 0 && resultSetIndex < List.length values -> List.length values.[resultSetIndex] | _ -> sprintf "count rows: out of resultSetIndex %i" resultSetIndex |> failwith - CountResultSets = List.length values - } + CountResultSets = List.length values } -let makeDependencies (valToParam: (string -> 'a -> 'b) option) logger = { - CreateCommand = (fun connection -> connection.CreateCommand()) - ExecuteReaderAsync = (fun cmd -> cmd.ExecuteReaderAsync()) - DbValueToParameter = valToParam |> Option.defaultValue (fun _ _ -> failwith "") - GlobalLogger = logger -} +let makeDependencies (valToParam: (string -> 'a -> 'b) option) logger = + { CreateCommand = (fun connection -> connection.CreateCommand()) + ExecuteReaderAsync = (fun cmd -> cmd.ExecuteReaderAsync()) + DbValueToParameter = valToParam |> Option.defaultValue (fun _ _ -> failwith "") + GlobalLogger = logger } let makeReader data _ = let mutable currentRowIndex = -1 diff --git a/Vp.FSharp.Sql.Tests/SqlCommand for executeNonQuery should.fs b/Vp.FSharp.Sql.Tests/SqlCommand for executeNonQuery should.fs index 098174c..40aae10 100644 --- a/Vp.FSharp.Sql.Tests/SqlCommand for executeNonQuery should.fs +++ b/Vp.FSharp.Sql.Tests/SqlCommand for executeNonQuery should.fs @@ -1,112 +1,83 @@ module Vp.FSharp.Sql.Tests.``SqlCommand for executeNonQuery should`` open System.Data + open Swensen.Unquote + open Xunit open Vp.FSharp.Sql +open Vp.FSharp.Sql.Tests.Helpers [] -let ``executeNonQuery should open and close the connection when it's closed`` () = - let openCall = ref 0 - let closeCall = ref 0 - let openCallback () = incr openCall - let closeCallback () = incr closeCall - +let ``open and then close the connection if initially closed`` () = + let callCounter = PartialCallCounter.initSame 0 + let (openCallback, closeCallback) = PartialCallCounter.createCallbacks callCounter async { use connection = Mocks.NonQuery 0 |> Mocks.makeConnection "toto" ConnectionState.Closed openCallback closeCallback - let! r = SqlCommand.text "update" - |> SqlCommand.noLogger - |> SqlCommand.executeNonQuery connection (Mocks.makeDependencies None None) + let! r = + SqlCommand.text "update" + |> SqlCommand.noLogger + |> SqlCommand.executeNonQuery connection (Mocks.makeDependencies None None) r =! 0 - !openCall =! 1 - !closeCall =! 1 + PartialCallCounter.assertEqual callCounter 1 1 } [] -let ``executeNonQuery should let the connection when it's other than closed`` () = - let openCall = ref 0 - let closeCall = ref 0 - let openCallback () = incr openCall - let closeCallback () = incr closeCall - +let ``leave the connection open if initially open`` () = + let callCounter = PartialCallCounter.initSame 0 + let (openCallback, closeCallback) = PartialCallCounter.createCallbacks callCounter async { use connection = Mocks.NonQuery 1 |> Mocks.makeConnection "toto" ConnectionState.Connecting openCallback closeCallback - let! r = SqlCommand.text "update" - |> SqlCommand.noLogger - |> SqlCommand.executeNonQuery connection (Mocks.makeDependencies None None) + let! r = + SqlCommand.text "update" + |> SqlCommand.noLogger + |> SqlCommand.executeNonQuery connection (Mocks.makeDependencies None None) r =! 1 - !openCall =! 0 - !closeCall =! 0 + PartialCallCounter.assertEqual callCounter 0 0 } [] -let ``executeNonQuery should log for all events on globalLogger when the connection is closed`` () = - let openCall = ref 0 - let closeCall = ref 0 - let openCallback () = incr openCall - let closeCallback () = incr closeCall - let connectionOpened = ref 0 - let connectionClosed = ref 0 - let commandPrepared = ref 0 - let commandExecuted = ref 0 - let loggerCallback = - function - | ConnectionOpened _ -> incr connectionOpened - | ConnectionClosed _ -> incr connectionClosed - | CommandPrepared _ -> incr commandPrepared - | CommandExecuted _ -> incr commandExecuted +let ``log for all events on globalLogger if connection initially closed`` () = + let callCounter = FullCallCounter.initSame 0 + let openCallback = FullCallCounter.createOpenCallback callCounter + let closeCallback = FullCallCounter.createCloseCallback callCounter + let loggerCallback = FullCallCounter.createLoggerCallback callCounter async { use connection = Mocks.NonQuery 2 |> Mocks.makeConnection "toto" ConnectionState.Closed openCallback closeCallback - let deps = Some loggerCallback - |> Mocks.makeDependencies None - let! r = SqlCommand.text "update" - |> SqlCommand.executeNonQuery connection deps + let deps = + Some loggerCallback + |> Mocks.makeDependencies None + let! r = + SqlCommand.text "update" + |> SqlCommand.executeNonQuery connection deps r =! 2 - !openCall =! 1 - !closeCall =! 1 - !connectionOpened =! 1 - !connectionClosed =! 1 - !commandPrepared =! 1 - !commandExecuted =! 1 + FullCallCounter.assertEqual callCounter 1 1 1 1 1 1 } [] -let ``executeNonQuery should log for just command events on globalLogger when the connection is NOT closed`` () = - let openCall = ref 0 - let closeCall = ref 0 - let openCallback () = incr openCall - let closeCallback () = incr closeCall - let connectionOpened = ref 0 - let connectionClosed = ref 0 - let commandPrepared = ref 0 - let commandExecuted = ref 0 - let loggerCallback = - function - | ConnectionOpened _ -> incr connectionOpened - | ConnectionClosed _ -> incr connectionClosed - | CommandPrepared _ -> incr commandPrepared - | CommandExecuted _ -> incr commandExecuted +let ``log just command events on globalLogger if connection initially not closed`` () = + let callCounter = FullCallCounter.initSame 0 + let openCallback = FullCallCounter.createOpenCallback callCounter + let closeCallback = FullCallCounter.createCloseCallback callCounter + let loggerCallback = FullCallCounter.createLoggerCallback callCounter async { use connection = Mocks.NonQuery 3 |> Mocks.makeConnection "toto" ConnectionState.Connecting openCallback closeCallback - let deps = Some loggerCallback - |> Mocks.makeDependencies None - let! r = SqlCommand.text "update" - |> SqlCommand.executeNonQuery connection deps + let deps = + Some loggerCallback + |> Mocks.makeDependencies None + let! r = + SqlCommand.text "update" + |> SqlCommand.executeNonQuery connection deps r =! 3 - !openCall =! 0 - !closeCall =! 0 - !connectionOpened =! 0 - !connectionClosed =! 0 - !commandPrepared =! 1 - !commandExecuted =! 1 + FullCallCounter.assertEqual callCounter 0 0 0 0 1 1 } diff --git a/Vp.FSharp.Sql.Tests/SqlCommand for executeScalar should.fs b/Vp.FSharp.Sql.Tests/SqlCommand for executeScalar should.fs index 29dc595..a2710e0 100644 --- a/Vp.FSharp.Sql.Tests/SqlCommand for executeScalar should.fs +++ b/Vp.FSharp.Sql.Tests/SqlCommand for executeScalar should.fs @@ -1,27 +1,27 @@ module Vp.FSharp.Sql.Tests.``SqlCommand for executeScalar should`` open System.Data + open Swensen.Unquote + open Xunit open Vp.FSharp.Sql +open Vp.FSharp.Sql.Tests.Helpers [] -let ``executeScalar should open and close the connection when it's closed`` () = - let openCall = ref 0 - let closeCall = ref 0 - let openCallback () = incr openCall - let closeCallback () = incr closeCall +let ``open and then close the connection if initially closed`` () = + let callCounter = PartialCallCounter.initSame 0 + let (openCallback, closeCallback) = PartialCallCounter.createCallbacks callCounter let data = Mocks.fakeData [[ [14] ]] [[ { Name = "id" - FieldType = typeof - NativeTypeName = typeof.Name - } + FieldType = typeof + NativeTypeName = typeof.Name } ]] async { @@ -32,25 +32,21 @@ let ``executeScalar should open and close the connection when it's closed`` () = |> SqlCommand.noLogger |> SqlCommand.executeScalar connection (Mocks.makeDependencies None None) r =! 14 - !openCall =! 1 - !closeCall =! 1 + PartialCallCounter.assertEqual callCounter 1 1 } [] -let ``executeScalar should let the connection when it's other than closed`` () = - let openCall = ref 0 - let closeCall = ref 0 - let openCallback () = incr openCall - let closeCallback () = incr closeCall +let ``leave the connection open if initially open`` () = + let callCounter = PartialCallCounter.initSame 0 + let (openCallback, closeCallback) = PartialCallCounter.createCallbacks callCounter let data = Mocks.fakeData [[ [15] ]] [[ { Name = "id" - FieldType = typeof - NativeTypeName = typeof.Name - } + FieldType = typeof + NativeTypeName = typeof.Name } ]] async { @@ -61,92 +57,63 @@ let ``executeScalar should let the connection when it's other than closed`` () = |> SqlCommand.noLogger |> SqlCommand.executeScalar connection (Mocks.makeDependencies None None) r =! 15 - !openCall =! 0 - !closeCall =! 0 + PartialCallCounter.assertEqual callCounter 0 0 } [] -let ``executeScalar should log for all events on globalLogger when the connection is closed`` () = - let openCall = ref 0 - let closeCall = ref 0 - let openCallback () = incr openCall - let closeCallback () = incr closeCall - let connectionOpened = ref 0 - let connectionClosed = ref 0 - let commandPrepared = ref 0 - let commandExecuted = ref 0 - let loggerCallback = - function - | ConnectionOpened _ -> incr connectionOpened - | ConnectionClosed _ -> incr connectionClosed - | CommandPrepared _ -> incr commandPrepared - | CommandExecuted _ -> incr commandExecuted +let ``log for all events on globalLogger if connection initially closed`` () = + let callCounter = FullCallCounter.initSame 0 + let openCallback = FullCallCounter.createOpenCallback callCounter + let closeCallback = FullCallCounter.createCloseCallback callCounter + let loggerCallback = FullCallCounter.createLoggerCallback callCounter let data = Mocks.fakeData [[ [16] ]] [[ { Name = "id" - FieldType = typeof - NativeTypeName = typeof.Name - } + FieldType = typeof + NativeTypeName = typeof.Name } ]] async { use connection = Mocks.Reader (Mocks.makeReader data) |> Mocks.makeConnection "toto" ConnectionState.Closed openCallback closeCallback - let deps = Some loggerCallback - |> Mocks.makeDependencies None - let! r = SqlCommand.text "select 1" - |> SqlCommand.executeScalar connection deps + let deps = + Some loggerCallback + |> Mocks.makeDependencies None + let! r = + SqlCommand.text "select 1" + |> SqlCommand.executeScalar connection deps r =! 16 - !openCall =! 1 - !closeCall =! 1 - !connectionOpened =! 1 - !connectionClosed =! 1 - !commandPrepared =! 1 - !commandExecuted =! 1 + FullCallCounter.assertEqual callCounter 1 1 1 1 1 1 } [] -let ``executeScalar should log for just command events on globalLogger when the connection is NOT closed`` () = - let openCall = ref 0 - let closeCall = ref 0 - let openCallback () = incr openCall - let closeCallback () = incr closeCall - let connectionOpened = ref 0 - let connectionClosed = ref 0 - let commandPrepared = ref 0 - let commandExecuted = ref 0 - let loggerCallback = - function - | ConnectionOpened _ -> incr connectionOpened - | ConnectionClosed _ -> incr connectionClosed - | CommandPrepared _ -> incr commandPrepared - | CommandExecuted _ -> incr commandExecuted +let ``log for just command events on globalLogger if connection initially not closed`` () = + let callCounter = FullCallCounter.initSame 0 + let openCallback = FullCallCounter.createOpenCallback callCounter + let closeCallback = FullCallCounter.createCloseCallback callCounter + let loggerCallback = FullCallCounter.createLoggerCallback callCounter let data = Mocks.fakeData [[ [17] ]] [[ { Name = "id" - FieldType = typeof - NativeTypeName = typeof.Name - } + FieldType = typeof + NativeTypeName = typeof.Name } ]] async { use connection = Mocks.Reader (Mocks.makeReader data) |> Mocks.makeConnection "toto" ConnectionState.Connecting openCallback closeCallback - let deps = Some loggerCallback - |> Mocks.makeDependencies None - let! r = SqlCommand.text "select 1" - |> SqlCommand.executeScalar connection deps + let deps = + Some loggerCallback + |> Mocks.makeDependencies None + let! r = + SqlCommand.text "select 1" + |> SqlCommand.executeScalar connection deps r =! 17 - !openCall =! 0 - !closeCall =! 0 - !connectionOpened =! 0 - !connectionClosed =! 0 - !commandPrepared =! 1 - !commandExecuted =! 1 + FullCallCounter.assertEqual callCounter 0 0 0 0 1 1 } diff --git a/Vp.FSharp.Sql.Tests/SqlCommand for executeScalarOrNone should.fs b/Vp.FSharp.Sql.Tests/SqlCommand for executeScalarOrNone should.fs index 5c17966..4772c9c 100644 --- a/Vp.FSharp.Sql.Tests/SqlCommand for executeScalarOrNone should.fs +++ b/Vp.FSharp.Sql.Tests/SqlCommand for executeScalarOrNone should.fs @@ -1,26 +1,27 @@ module Vp.FSharp.Sql.Tests.``SqlCommand for executeScalarOrNone should`` open System.Data + open Swensen.Unquote + open Xunit open Vp.FSharp.Sql +open Vp.FSharp.Sql.Tests.Helpers [] -let ``executeScalarOrNone should open and close the connection when it's closed`` () = - let openCall = ref 0 - let closeCall = ref 0 - let openCallback () = incr openCall - let closeCallback () = incr closeCall +let ``open and then close the connection if initially closed`` () = + let callCounter = PartialCallCounter.initSame 0 + let (openCallback, closeCallback) = PartialCallCounter.createCallbacks callCounter let data = Mocks.fakeData [[ [14] ]] [[ { Name = "id" - FieldType = typeof - NativeTypeName = typeof.Name + FieldType = typeof + NativeTypeName = typeof.Name } ]] @@ -35,24 +36,21 @@ let ``executeScalarOrNone should open and close the connection when it's closed` r |> Option.defaultValue 42 |> (=!) 14 - !openCall =! 1 - !closeCall =! 1 + PartialCallCounter.assertEqual callCounter 1 1 } [] -let ``executeScalarOrNone should let the connection when it's other than closed`` () = - let openCall = ref 0 - let closeCall = ref 0 - let openCallback () = incr openCall - let closeCallback () = incr closeCall +let ``leave the connection open if initially not closed`` () = + let callCounter = PartialCallCounter.initSame 0 + let (openCallback, closeCallback) = PartialCallCounter.createCallbacks callCounter let data = Mocks.fakeData [[ [15] ]] [[ { Name = "id" - FieldType = typeof - NativeTypeName = typeof.Name + FieldType = typeof + NativeTypeName = typeof.Name } ]] @@ -68,35 +66,23 @@ let ``executeScalarOrNone should let the connection when it's other than closed` |> Option.defaultValue 42 |> (=!) 15 - !openCall =! 0 - !closeCall =! 0 + PartialCallCounter.assertEqual callCounter 0 0 } [] -let ``executeScalarOrNone should log for all events on globalLogger when the connection is closed`` () = - let openCall = ref 0 - let closeCall = ref 0 - let openCallback () = incr openCall - let closeCallback () = incr closeCall - let connectionOpened = ref 0 - let connectionClosed = ref 0 - let commandPrepared = ref 0 - let commandExecuted = ref 0 - let loggerCallback = - function - | ConnectionOpened _ -> incr connectionOpened - | ConnectionClosed _ -> incr connectionClosed - | CommandPrepared _ -> incr commandPrepared - | CommandExecuted _ -> incr commandExecuted +let ``log for all events on globalLogger if connection initially closed`` () = + let callCounter = FullCallCounter.initSame 0 + let openCallback = FullCallCounter.createOpenCallback callCounter + let closeCallback = FullCallCounter.createCloseCallback callCounter + let loggerCallback = FullCallCounter.createLoggerCallback callCounter let data = Mocks.fakeData [[ [16] ]] [[ { Name = "id" - FieldType = typeof - NativeTypeName = typeof.Name - } + FieldType = typeof + NativeTypeName = typeof.Name } ]] async { use connection = @@ -110,39 +96,23 @@ let ``executeScalarOrNone should log for all events on globalLogger when the con r |> Option.defaultValue 42 |> (=!) 16 - !openCall =! 1 - !closeCall =! 1 - !connectionOpened =! 1 - !connectionClosed =! 1 - !commandPrepared =! 1 - !commandExecuted =! 1 + FullCallCounter.assertEqual callCounter 1 1 1 1 1 1 } [] -let ``executeScalarOrNone should log for just command events on globalLogger when the connection is NOT closed`` () = - let openCall = ref 0 - let closeCall = ref 0 - let openCallback () = incr openCall - let closeCallback () = incr closeCall - let connectionOpened = ref 0 - let connectionClosed = ref 0 - let commandPrepared = ref 0 - let commandExecuted = ref 0 - let loggerCallback = - function - | ConnectionOpened _ -> incr connectionOpened - | ConnectionClosed _ -> incr connectionClosed - | CommandPrepared _ -> incr commandPrepared - | CommandExecuted _ -> incr commandExecuted +let ``log for just command events on globalLogger if connection initially not closed`` () = + let callCounter = FullCallCounter.initSame 0 + let openCallback = FullCallCounter.createOpenCallback callCounter + let closeCallback = FullCallCounter.createCloseCallback callCounter + let loggerCallback = FullCallCounter.createLoggerCallback callCounter let data = Mocks.fakeData [[ [17] ]] [[ { Name = "id" - FieldType = typeof - NativeTypeName = typeof.Name - } + FieldType = typeof + NativeTypeName = typeof.Name } ]] async { use connection = @@ -156,30 +126,22 @@ let ``executeScalarOrNone should log for just command events on globalLogger whe r |> Option.defaultValue 42 |> (=!) 17 - !openCall =! 0 - !closeCall =! 0 - !connectionOpened =! 0 - !connectionClosed =! 0 - !commandPrepared =! 1 - !commandExecuted =! 1 + FullCallCounter.assertEqual callCounter 0 0 0 0 1 1 } [] -let ``executeScalarOrNone should open and close the connection when it's closed and retrieve None`` () = - let openCall = ref 0 - let closeCall = ref 0 - let openCallback () = incr openCall - let closeCallback () = incr closeCall +let ``open and then close connection if initially closed and retrieve None`` () = + let callCounter = PartialCallCounter.initSame 0 + let (openCallback, closeCallback) = PartialCallCounter.createCallbacks callCounter let data = Mocks.fakeData [[ [null] ]] [[ { Name = "id" - FieldType = typeof - NativeTypeName = typeof.Name - } + FieldType = typeof + NativeTypeName = typeof.Name } ]] async { @@ -190,25 +152,21 @@ let ``executeScalarOrNone should open and close the connection when it's closed |> SqlCommand.noLogger |> SqlCommand.executeScalarOrNone connection (Mocks.makeDependencies None None) r.IsNone =! true - !openCall =! 1 - !closeCall =! 1 + PartialCallCounter.assertEqual callCounter 1 1 } [] -let ``executeScalarOrNone should let the connection when it's other than closed and retrieve None`` () = - let openCall = ref 0 - let closeCall = ref 0 - let openCallback () = incr openCall - let closeCallback () = incr closeCall +let ``leave connection open if initially not closed and retrieve None`` () = + let callCounter = PartialCallCounter.initSame 0 + let (openCallback, closeCallback) = PartialCallCounter.createCallbacks callCounter let data = Mocks.fakeData [[ [null] ]] [[ { Name = "id" - FieldType = typeof - NativeTypeName = typeof.Name - } + FieldType = typeof + NativeTypeName = typeof.Name } ]] async { @@ -219,35 +177,23 @@ let ``executeScalarOrNone should let the connection when it's other than closed |> SqlCommand.noLogger |> SqlCommand.executeScalarOrNone connection (Mocks.makeDependencies None None) r.IsNone =! true - !openCall =! 0 - !closeCall =! 0 + PartialCallCounter.assertEqual callCounter 0 0 } [] -let ``executeScalarOrNone should log for all events on globalLogger when the connection is closed and retrieve None`` () = - let openCall = ref 0 - let closeCall = ref 0 - let openCallback () = incr openCall - let closeCallback () = incr closeCall - let connectionOpened = ref 0 - let connectionClosed = ref 0 - let commandPrepared = ref 0 - let commandExecuted = ref 0 - let loggerCallback = - function - | ConnectionOpened _ -> incr connectionOpened - | ConnectionClosed _ -> incr connectionClosed - | CommandPrepared _ -> incr commandPrepared - | CommandExecuted _ -> incr commandExecuted +let ``log for all events on globalLogger if connection initially closed and retrieve None`` () = + let callCounter = FullCallCounter.initSame 0 + let openCallback = FullCallCounter.createOpenCallback callCounter + let closeCallback = FullCallCounter.createCloseCallback callCounter + let loggerCallback = FullCallCounter.createLoggerCallback callCounter let data = Mocks.fakeData [[ [null] ]] [[ { Name = "id" - FieldType = typeof - NativeTypeName = typeof.Name - } + FieldType = typeof + NativeTypeName = typeof.Name } ]] async { use connection = @@ -258,39 +204,23 @@ let ``executeScalarOrNone should log for all events on globalLogger when the con let! r = SqlCommand.text "select 1" |> SqlCommand.executeScalarOrNone connection deps r.IsNone =! true - !openCall =! 1 - !closeCall =! 1 - !connectionOpened =! 1 - !connectionClosed =! 1 - !commandPrepared =! 1 - !commandExecuted =! 1 + FullCallCounter.assertEqual callCounter 1 1 1 1 1 1 } [] -let ``executeScalarOrNone should log for just command events on globalLogger when the connection is NOT closed and retrieve None`` () = - let openCall = ref 0 - let closeCall = ref 0 - let openCallback () = incr openCall - let closeCallback () = incr closeCall - let connectionOpened = ref 0 - let connectionClosed = ref 0 - let commandPrepared = ref 0 - let commandExecuted = ref 0 - let loggerCallback = - function - | ConnectionOpened _ -> incr connectionOpened - | ConnectionClosed _ -> incr connectionClosed - | CommandPrepared _ -> incr commandPrepared - | CommandExecuted _ -> incr commandExecuted +let ``log for just command events on globalLogger if connection initially not closed and retrieve None`` () = + let callCounter = FullCallCounter.initSame 0 + let openCallback = FullCallCounter.createOpenCallback callCounter + let closeCallback = FullCallCounter.createCloseCallback callCounter + let loggerCallback = FullCallCounter.createLoggerCallback callCounter let data = Mocks.fakeData [[ [null] ]] [[ { Name = "id" - FieldType = typeof - NativeTypeName = typeof.Name - } + FieldType = typeof + NativeTypeName = typeof.Name } ]] async { use connection = @@ -301,10 +231,5 @@ let ``executeScalarOrNone should log for just command events on globalLogger whe let! r = SqlCommand.text "select 1" |> SqlCommand.executeScalarOrNone connection deps r.IsNone =! true - !openCall =! 0 - !closeCall =! 0 - !connectionOpened =! 0 - !connectionClosed =! 0 - !commandPrepared =! 1 - !commandExecuted =! 1 + FullCallCounter.assertEqual callCounter 0 0 0 0 1 1 } diff --git a/Vp.FSharp.Sql.Tests/SqlCommand for queryAsyncSeq should.fs b/Vp.FSharp.Sql.Tests/SqlCommand for queryAsyncSeq should.fs index 5ac7c42..50f44fe 100644 --- a/Vp.FSharp.Sql.Tests/SqlCommand for queryAsyncSeq should.fs +++ b/Vp.FSharp.Sql.Tests/SqlCommand for queryAsyncSeq should.fs @@ -3,11 +3,15 @@ module Vp.FSharp.Sql.Tests.``SqlCommand for queryAsyncSeq should`` open System open System.Data open System.Data.Common + open FSharp.Control + open Swensen.Unquote + open Xunit open Vp.FSharp.Sql +open Vp.FSharp.Sql.Tests.Helpers let toFieldName = @@ -18,26 +22,24 @@ let toFieldNames = let readValueByFieldName (columns: string list) _ _ (reader: SqlRecordReader) = columns - |> List.map (fun fieldName -> (fieldName, reader.Value fieldName |> int)) + |> List.map (fun fieldName -> (fieldName, reader.Value fieldName |> int32)) let readValueByIndex (columns: int32 list) _ _ (reader: SqlRecordReader) = columns - |> List.map (reader.Value >> int) + |> List.map (reader.Value >> int32) let readValueOrNoneByFieldName (columns: string list) _ _ (reader: SqlRecordReader) = columns - |> List.map (fun fieldName -> (fieldName, reader.ValueOrNone fieldName |> Option.map int)) + |> List.map (fun fieldName -> (fieldName, reader.ValueOrNone fieldName |> Option.map int32)) let readValueOrNoneByIndex (columns: int32 list) _ _ (reader: SqlRecordReader) = columns - |> List.map (reader.ValueOrNone >> Option.map int) + |> List.map (reader.ValueOrNone >> Option.map int32) [] -let ``queryAsyncSeq should open and close the connection when it's closed and access value by columnName`` () = - let openCall = ref 0 - let closeCall = ref 0 - let openCallback () = incr openCall - let closeCallback () = incr closeCall +let ``open and then close the connection if initially closed and access value by columnName`` () = + let callCounter = PartialCallCounter.initSame 0 + let (openCallback, closeCallback) = PartialCallCounter.createCallbacks callCounter let data = Mocks.fakeData [[ [1;2;3] @@ -45,16 +47,16 @@ let ``queryAsyncSeq should open and close the connection when it's closed and ac ]] [[ { Name = "id0" - FieldType = typeof - NativeTypeName = typeof.Name + FieldType = typeof + NativeTypeName = typeof.Name } { Name = "id1" - FieldType = typeof - NativeTypeName = typeof.Name + FieldType = typeof + NativeTypeName = typeof.Name } { Name = "id2" - FieldType = typeof - NativeTypeName = typeof.Name + FieldType = typeof + NativeTypeName = typeof.Name } ]] async { @@ -69,16 +71,13 @@ let ``queryAsyncSeq should open and close the connection when it's closed and ac |> List.sortBy (fun values -> snd values.[0]) r.Length =! 2 r =! [[("id2", 3);("id1", 2);("id0", 1)];[("id2", 6);("id1", 5);("id0", 4)]] - !openCall =! 1 - !closeCall =! 1 + PartialCallCounter.assertEqual callCounter 1 1 } [] -let ``queryAsyncSeq should open and close the connection when it's closed and access value by ordinal`` () = - let openCall = ref 0 - let closeCall = ref 0 - let openCallback () = incr openCall - let closeCallback () = incr closeCall +let ``open and then close the connection if initially closed and access value by ordinal`` () = + let callCounter = PartialCallCounter.initSame 0 + let (openCallback, closeCallback) = PartialCallCounter.createCallbacks callCounter let data = Mocks.fakeData [[ [1;2;3] @@ -86,16 +85,16 @@ let ``queryAsyncSeq should open and close the connection when it's closed and ac ]] [[ { Name = "id0" - FieldType = typeof - NativeTypeName = typeof.Name + FieldType = typeof + NativeTypeName = typeof.Name } { Name = "id1" - FieldType = typeof - NativeTypeName = typeof.Name + FieldType = typeof + NativeTypeName = typeof.Name } { Name = "id2" - FieldType = typeof - NativeTypeName = typeof.Name + FieldType = typeof + NativeTypeName = typeof.Name } ]] async { @@ -110,16 +109,13 @@ let ``queryAsyncSeq should open and close the connection when it's closed and ac |> List.sortBy (fun values -> values.[0]) r.Length =! 2 r =! [[3;2;1];[6;5;4]] - !openCall =! 1 - !closeCall =! 1 + PartialCallCounter.assertEqual callCounter 1 1 } [] -let ``queryAsyncSeq should open and close the connection when it's closed and access valueOrNone by columnName`` () = - let openCall = ref 0 - let closeCall = ref 0 - let openCallback () = incr openCall - let closeCallback () = incr closeCall +let ``open and then close the connection if initially closed and access valueOrNone by columnName`` () = + let callCounter = PartialCallCounter.initSame 0 + let (openCallback, closeCallback) = PartialCallCounter.createCallbacks callCounter let data = Mocks.fakeData [[ [1;null;3] @@ -151,16 +147,13 @@ let ``queryAsyncSeq should open and close the connection when it's closed and ac |> List.sortBy (fun values -> snd values.[0]) r.Length =! 2 r =! [[("id2", Some 3);("id1", None);("id0", Some 1)];[("id2", Some 6);("id1", Some 5);("id0", Some 4)]] - !openCall =! 1 - !closeCall =! 1 + PartialCallCounter.assertEqual callCounter 1 1 } [] -let ``queryAsyncSeq should open and close the connection when it's closed and access valueOrNone by ordinal`` () = - let openCall = ref 0 - let closeCall = ref 0 - let openCallback () = incr openCall - let closeCallback () = incr closeCall +let ``open and then close connection if initially closed and access valueOrNone by ordinal`` () = + let callCounter = PartialCallCounter.initSame 0 + let (openCallback, closeCallback) = PartialCallCounter.createCallbacks callCounter let data = Mocks.fakeData [[ [1;null;3] @@ -168,16 +161,16 @@ let ``queryAsyncSeq should open and close the connection when it's closed and ac ]] [[ { Name = "id0" - FieldType = typeof - NativeTypeName = typeof.Name + FieldType = typeof + NativeTypeName = typeof.Name } { Name = "id1" - FieldType = typeof - NativeTypeName = typeof.Name + FieldType = typeof + NativeTypeName = typeof.Name } { Name = "id2" - FieldType = typeof - NativeTypeName = typeof.Name + FieldType = typeof + NativeTypeName = typeof.Name } ]] async { @@ -192,16 +185,13 @@ let ``queryAsyncSeq should open and close the connection when it's closed and ac |> List.sortBy (fun values -> values.[0]) r.Length =! 2 r =! [[Some 3; None; Some 1];[Some 6; Some 5; Some 4]] - !openCall =! 1 - !closeCall =! 1 + PartialCallCounter.assertEqual callCounter 1 1 } [] -let ``queryAsyncSeq should let the connection when it's other than closed with access valueOrNone by ordinal`` () = - let openCall = ref 0 - let closeCall = ref 0 - let openCallback () = incr openCall - let closeCallback () = incr closeCall +let ``leave the connection open if initially not closed with access valueOrNone by ordinal`` () = + let callCounter = PartialCallCounter.initSame 0 + let (openCallback, closeCallback) = PartialCallCounter.createCallbacks callCounter let data = Mocks.fakeData [[ [1;null;3] @@ -209,16 +199,16 @@ let ``queryAsyncSeq should let the connection when it's other than closed with a ]] [[ { Name = "id0" - FieldType = typeof - NativeTypeName = typeof.Name + FieldType = typeof + NativeTypeName = typeof.Name } { Name = "id1" - FieldType = typeof - NativeTypeName = typeof.Name + FieldType = typeof + NativeTypeName = typeof.Name } { Name = "id2" - FieldType = typeof - NativeTypeName = typeof.Name + FieldType = typeof + NativeTypeName = typeof.Name } ]] async { @@ -233,26 +223,15 @@ let ``queryAsyncSeq should let the connection when it's other than closed with a |> List.sortBy (fun values -> values.[0]) r.Length =! 2 r =! [[Some 3; None; Some 1];[Some 6; Some 5; Some 4]] - !openCall =! 0 - !closeCall =! 0 + PartialCallCounter.assertEqual callCounter 0 0 } [] -let ``queryAsyncSeq should log for all events on globalLogger when the connection is closed with access valueOrNone by ordinal`` () = - let openCall = ref 0 - let closeCall = ref 0 - let openCallback () = incr openCall - let closeCallback () = incr closeCall - let connectionOpened = ref 0 - let connectionClosed = ref 0 - let commandPrepared = ref 0 - let commandExecuted = ref 0 - let loggerCallback = - function - | ConnectionOpened _ -> incr connectionOpened - | ConnectionClosed _ -> incr connectionClosed - | CommandPrepared _ -> incr commandPrepared - | CommandExecuted _ -> incr commandExecuted +let ``log all events on globalLogger if connection initially closed with access valueOrNone by ordinal`` () = + let callCounter = FullCallCounter.initSame 0 + let openCallback = FullCallCounter.createOpenCallback callCounter + let closeCallback = FullCallCounter.createCloseCallback callCounter + let loggerCallback = FullCallCounter.createLoggerCallback callCounter let data = Mocks.fakeData [[ [1;null;3] @@ -260,16 +239,16 @@ let ``queryAsyncSeq should log for all events on globalLogger when the connectio ]] [[ { Name = "id0" - FieldType = typeof - NativeTypeName = typeof.Name + FieldType = typeof + NativeTypeName = typeof.Name } { Name = "id1" - FieldType = typeof - NativeTypeName = typeof.Name + FieldType = typeof + NativeTypeName = typeof.Name } { Name = "id2" - FieldType = typeof - NativeTypeName = typeof.Name + FieldType = typeof + NativeTypeName = typeof.Name } ]] async { @@ -285,30 +264,15 @@ let ``queryAsyncSeq should log for all events on globalLogger when the connectio |> List.sortBy (fun values -> values.[0]) r.Length =! 2 r =! [[Some 3; None; Some 1];[Some 6; Some 5; Some 4]] - !openCall =! 1 - !closeCall =! 1 - !connectionOpened =! 1 - !connectionClosed =! 1 - !commandPrepared =! 1 - !commandExecuted =! 1 + FullCallCounter.assertEqual callCounter 1 1 1 1 1 1 } [] -let ``queryAsyncSeq should log for just command events on globalLogger when the connection is NOT closed with access valueOrNone by ordinal`` () = - let openCall = ref 0 - let closeCall = ref 0 - let openCallback () = incr openCall - let closeCallback () = incr closeCall - let connectionOpened = ref 0 - let connectionClosed = ref 0 - let commandPrepared = ref 0 - let commandExecuted = ref 0 - let loggerCallback = - function - | ConnectionOpened _ -> incr connectionOpened - | ConnectionClosed _ -> incr connectionClosed - | CommandPrepared _ -> incr commandPrepared - | CommandExecuted _ -> incr commandExecuted +let ``log for just command events on globalLogger if connection initially not closed with access valueOrNone by ordinal`` () = + let callCounter = FullCallCounter.initSame 0 + let openCallback = FullCallCounter.createOpenCallback callCounter + let closeCallback = FullCallCounter.createCloseCallback callCounter + let loggerCallback = FullCallCounter.createLoggerCallback callCounter let data = Mocks.fakeData [[ [1;null;3] @@ -316,16 +280,16 @@ let ``queryAsyncSeq should log for just command events on globalLogger when the ]] [[ { Name = "id0" - FieldType = typeof - NativeTypeName = typeof.Name + FieldType = typeof + NativeTypeName = typeof.Name } { Name = "id1" - FieldType = typeof - NativeTypeName = typeof.Name + FieldType = typeof + NativeTypeName = typeof.Name } { Name = "id2" - FieldType = typeof - NativeTypeName = typeof.Name + FieldType = typeof + NativeTypeName = typeof.Name } ]] async { @@ -341,10 +305,5 @@ let ``queryAsyncSeq should log for just command events on globalLogger when the |> List.sortBy (fun values -> values.[0]) r.Length =! 2 r =! [[Some 3; None; Some 1];[Some 6; Some 5; Some 4]] - !openCall =! 0 - !closeCall =! 0 - !connectionOpened =! 0 - !connectionClosed =! 0 - !commandPrepared =! 1 - !commandExecuted =! 1 + FullCallCounter.assertEqual callCounter 0 0 0 0 1 1 } diff --git a/Vp.FSharp.Sql.Tests/SqlCommand for querySetListN should.fs b/Vp.FSharp.Sql.Tests/SqlCommand for querySetListN should.fs index f1a72de..6d2b20c 100644 --- a/Vp.FSharp.Sql.Tests/SqlCommand for querySetListN should.fs +++ b/Vp.FSharp.Sql.Tests/SqlCommand for querySetListN should.fs @@ -2,10 +2,13 @@ module Vp.FSharp.Sql.Tests.``SqlCommand for querySetListN should`` open System.Data open System.Data.Common + open Swensen.Unquote + open Xunit open Vp.FSharp.Sql +open Vp.FSharp.Sql.Tests.Helpers let private toFieldName = @@ -16,27 +19,22 @@ let private boxes values = let private makeDbField index : Mocks.DbField' = { Name = toFieldName index - FieldType = typeof - NativeTypeName = typeof.Name - } + FieldType = typeof + NativeTypeName = typeof.Name } let private readValueByIndex (columns: int32 list list) indexColumn _ (reader: SqlRecordReader) = columns.[indexColumn] - |> List.map (reader.Value >> int) + |> List.map (reader.Value >> int32) [] -let ``querySetList should open and close the connection when it's closed and access value by columnName`` () = - let openCall = ref 0 - let closeCall = ref 0 - let openCallback () = incr openCall - let closeCallback () = incr closeCall +let ``have querySetList should open and then close the connection if initially closed and access value by columnName`` () = + let callCounter = PartialCallCounter.initSame 0 + let (openCallback, closeCallback) = PartialCallCounter.createCallbacks callCounter let data = Mocks.fakeData [[1..6] |> boxes |> List.splitInto 2 - [1..8] |> boxes |> List.splitInto 2 - ] + [1..8] |> boxes |> List.splitInto 2] [[0..(0+2)] |> List.map makeDbField - [4..(4+3)] |> List.map makeDbField - ] + [4..(4+3)] |> List.map makeDbField] let columnsIndex = data.Columns |> List.map (List.mapi (fun i _ -> i)) async { use connection = @@ -45,27 +43,23 @@ let ``querySetList should open and close the connection when it's closed and acc let! r0 = SqlCommand.text "select 1" |> SqlCommand.noLogger - |> SqlCommand.querySetList connection (Mocks.makeDependencies None None) + |> SqlCommand.querySetList connection + (Mocks.makeDependencies None None) (readValueByIndex columnsIndex 0) r0.Length =! 2 r0 =! ([1..6] |> List.splitInto 2) - !openCall =! 1 - !closeCall =! 1 + PartialCallCounter.assertEqual callCounter 1 1 } [] -let ``querySetList2 should open and close the connection when it's closed and access value by columnName`` () = - let openCall = ref 0 - let closeCall = ref 0 - let openCallback () = incr openCall - let closeCallback () = incr closeCall +let ``have querySetList2 should open and then close the connection if initially closed and access value by columnName`` () = + let callCounter = PartialCallCounter.initSame 0 + let (openCallback, closeCallback) = PartialCallCounter.createCallbacks callCounter let data = Mocks.fakeData [[1..6] |> boxes |> List.splitInto 2 - [1..8] |> boxes |> List.splitInto 2 - ] + [1..8] |> boxes |> List.splitInto 2] [[0..(0+2)] |> List.map makeDbField - [4..(4+3)] |> List.map makeDbField - ] + [4..(4+3)] |> List.map makeDbField] let columnsIndex = data.Columns |> List.map (List.mapi (fun i _ -> i)) async { use connection = @@ -81,44 +75,38 @@ let ``querySetList2 should open and close the connection when it's closed and ac r0 =! ([1..6] |> List.splitInto 2) r1.Length =! 2 r1 =! ([1..8] |> List.splitInto 2) - !openCall =! 1 - !closeCall =! 1 + PartialCallCounter.assertEqual callCounter 1 1 } [] -let ``querySetList3 should open and close the connection when it's closed and access value by columnName`` () = - let openCall = ref 0 - let closeCall = ref 0 - let openCallback () = incr openCall - let closeCallback () = incr closeCall +let ``have querySetList3 open and then close the connection if initially closed and access value by columnName`` () = + let callCounter = PartialCallCounter.initSame 0 + let (openCallback, closeCallback) = PartialCallCounter.createCallbacks callCounter let data = Mocks.fakeData [[1..6] |> boxes |> List.splitInto 2 [1..8] |> boxes |> List.splitInto 2 - [1..10] |> boxes |> List.splitInto 2 - ] + [1..10] |> boxes |> List.splitInto 2] [[0..(0+2)] |> List.map makeDbField [4..(4+3)] |> List.map makeDbField - [8..(8+4)] |> List.map makeDbField - ] + [8..(8+4)] |> List.map makeDbField] let columnsIndex = data.Columns |> List.map (List.mapi (fun i _ -> i)) async { use connection = Mocks.Reader (Mocks.makeReader data) |> Mocks.makeConnection "toto" ConnectionState.Closed openCallback closeCallback let! (r0, r1, r2) = - SqlCommand.text "select 1" - |> SqlCommand.noLogger - |> SqlCommand.querySetList3 connection (Mocks.makeDependencies None None) - (readValueByIndex columnsIndex 0) - (readValueByIndex columnsIndex 1) - (readValueByIndex columnsIndex 2) + SqlCommand.text "select 1" + |> SqlCommand.noLogger + |> SqlCommand.querySetList3 connection (Mocks.makeDependencies None None) + (readValueByIndex columnsIndex 0) + (readValueByIndex columnsIndex 1) + (readValueByIndex columnsIndex 2) r0.Length =! 2 r0 =! ([1..6] |> List.splitInto 2) r1.Length =! 2 r1 =! ([1..8] |> List.splitInto 2) r2.Length =! 2 r2 =! ([1..10] |> List.splitInto 2) - !openCall =! 1 - !closeCall =! 1 + PartialCallCounter.assertEqual callCounter 1 1 } diff --git a/Vp.FSharp.Sql.Tests/Vp.FSharp.Sql.Tests.fsproj b/Vp.FSharp.Sql.Tests/Vp.FSharp.Sql.Tests.fsproj index 5f1dab3..69a17fc 100644 --- a/Vp.FSharp.Sql.Tests/Vp.FSharp.Sql.Tests.fsproj +++ b/Vp.FSharp.Sql.Tests/Vp.FSharp.Sql.Tests.fsproj @@ -9,6 +9,7 @@ + diff --git a/Vp.FSharp.Sql/Helpers.fs b/Vp.FSharp.Sql/Helpers.fs index fb7a019..d4f6ebd 100644 --- a/Vp.FSharp.Sql/Helpers.fs +++ b/Vp.FSharp.Sql/Helpers.fs @@ -12,7 +12,17 @@ open FSharp.Control type internal DbConnection with - member internal this.EnlistCurrentTransaction() = this.EnlistTransaction(Transaction.Current) + member this.EnlistCurrentTransaction() = this.EnlistTransaction(Transaction.Current) + +type internal DbDataReader with + member this.AwaitRead(cancellationToken) = this.ReadAsync(cancellationToken) |> Async.AwaitTask + member this.AwaitNextResult(cancellationToken) = this.NextResultAsync(cancellationToken) |> Async.AwaitTask + member this.AwaitTryReadNextResult(cancellationToken) = + async { + let! nextResultOk = this.AwaitNextResult(cancellationToken) + if nextResultOk then return! this.AwaitRead(cancellationToken) + else return nextResultOk + } [] @@ -50,20 +60,31 @@ module internal Async = return mergedTokenSource.Token } + +[] +module internal SkipFirstAsyncSeq = + + let scan folder state source = + AsyncSeq.scan folder state source + |> AsyncSeq.skip(1) + + let scanAsync folder state source = + AsyncSeq.scanAsync folder state source + |> AsyncSeq.skip(1) + [] module internal AsyncSeq = let mapbi mapping source = source - |> AsyncSeq.scan(fun state item -> (fst state + 1, item)) (-1, def) - |> AsyncSeq.skip(1) + |> SkipFirstAsyncSeq.scan(fun state item -> (fst state + 1I, item)) (-1I, def) |> AsyncSeq.map(fun (bi, item) -> mapping bi item) let mapChange selector mapping source = source |> mapbi (fun bi item -> (bi, item)) |> AsyncSeq.scan(fun (previousSelection, previousMappedItem, _) (bi, item) -> - if bi = 0 then + if bi = 0I then (selector item, mapping item, item) else let currentSelection = selector item diff --git a/Vp.FSharp.Sql/SqlCommand.fs b/Vp.FSharp.Sql/SqlCommand.fs index 981de46..aeac001 100644 --- a/Vp.FSharp.Sql/SqlCommand.fs +++ b/Vp.FSharp.Sql/SqlCommand.fs @@ -97,25 +97,6 @@ module Vp.FSharp.Sql.SqlCommand connection.OpenAsync(cancellationToken) |> Async.AwaitTask - type private ReadState = { Continue: bool; SetIndex: int32; RecordIndex: int32 } - - let private readNextResultRecord state (dataReader: #DbDataReader) cancellationToken = - async { - let! nextRecordOk = dataReader.ReadAsync(cancellationToken) |> Async.AwaitTask - if nextRecordOk then - return { state with RecordIndex = state.RecordIndex + 1 } - else - let! nextResultSetOk = dataReader.NextResultAsync(cancellationToken) |> Async.AwaitTask - if nextResultSetOk then - let! firstNextResultSetRecordOk = dataReader.ReadAsync(cancellationToken) |> Async.AwaitTask - if firstNextResultSetRecordOk then - return { state with SetIndex = state.SetIndex + 1; RecordIndex = 0 } - else - return { state with Continue = false } - else - return { state with Continue = false } - } - let private log4 deps commandDefinition sqlLog = match commandDefinition.Logger with | Global -> deps.GlobalLogger @@ -123,6 +104,28 @@ module Vp.FSharp.Sql.SqlCommand | Nothing -> None |> Option.iter (fun f -> f sqlLog) + type private ReadState = { Continue: bool; SetIndex: int32; RecordIndex: int32 } + + [] + module private ReadState = + let nextRecord state = { state with RecordIndex = state.RecordIndex + 1 } + let nextSet state = { state with SetIndex = state.SetIndex + 1; RecordIndex = 0 } + let stop state = { state with Continue = false } + + let private tryReadNextResultRecord state (dataReader: #DbDataReader) cancellationToken = + async { + let! nextResultReadOk = dataReader.AwaitTryReadNextResult(cancellationToken) + if nextResultReadOk then return ReadState.nextSet state + else return ReadState.stop state + } + + let private readNextRecord state (dataReader: #DbDataReader) cancellationToken = + async { + let! readOk = dataReader.AwaitRead(cancellationToken) + if readOk then return ReadState.nextRecord state + else return! tryReadNextResultRecord state dataReader cancellationToken + } + /// Return the sets of rows as an AsyncSeq accordingly to the command definition. let queryAsyncSeq (connection: #DbConnection) deps read commandDefinition = asyncSeq { @@ -144,11 +147,10 @@ module Vp.FSharp.Sql.SqlCommand use! dbDataReader = command.ExecuteReaderAsync(linkedToken) |> Async.AwaitTask let items = AsyncSeq.initInfinite(fun _ -> (dbDataReader, linkedToken)) - |> AsyncSeq.scanAsync( + |> SkipFirstAsyncSeq.scanAsync( fun state (dataReader, cancellationToken) -> - readNextResultRecord state dataReader cancellationToken ) + readNextRecord state dataReader cancellationToken ) { Continue = true; SetIndex = 0; RecordIndex = -1 } - |> AsyncSeq.skip(1) |> AsyncSeq.takeWhile(fun state -> state.Continue) |> AsyncSeq.mapChange(fun state -> state.SetIndex) (fun _ -> SqlRecordReader(dbDataReader)) |> AsyncSeq.mapAsync(fun (state, rowReader) -> async { From ce87d6764927b0682831aa48108d3b4b154cc98b Mon Sep 17 00:00:00 2001 From: Kerry Perret Date: Wed, 6 Jan 2021 08:24:55 +0100 Subject: [PATCH 2/4] feat(tests): more ref-- --- .../SqlCommand for executeNonQuery should.fs | 8 ++------ .../SqlCommand for executeScalar should.fs | 8 ++------ .../SqlCommand for executeScalarOrNone should.fs | 16 ++++------------ .../SqlCommand for queryAsyncSeq should.fs | 8 ++------ 4 files changed, 10 insertions(+), 30 deletions(-) diff --git a/Vp.FSharp.Sql.Tests/SqlCommand for executeNonQuery should.fs b/Vp.FSharp.Sql.Tests/SqlCommand for executeNonQuery should.fs index 40aae10..a2c77c7 100644 --- a/Vp.FSharp.Sql.Tests/SqlCommand for executeNonQuery should.fs +++ b/Vp.FSharp.Sql.Tests/SqlCommand for executeNonQuery should.fs @@ -45,9 +45,7 @@ let ``leave the connection open if initially open`` () = [] let ``log for all events on globalLogger if connection initially closed`` () = let callCounter = FullCallCounter.initSame 0 - let openCallback = FullCallCounter.createOpenCallback callCounter - let closeCallback = FullCallCounter.createCloseCallback callCounter - let loggerCallback = FullCallCounter.createLoggerCallback callCounter + let (openCallback, closeCallback, loggerCallback) = FullCallCounter.createCallbacks callCounter async { use connection = Mocks.NonQuery 2 @@ -65,9 +63,7 @@ let ``log for all events on globalLogger if connection initially closed`` () = [] let ``log just command events on globalLogger if connection initially not closed`` () = let callCounter = FullCallCounter.initSame 0 - let openCallback = FullCallCounter.createOpenCallback callCounter - let closeCallback = FullCallCounter.createCloseCallback callCounter - let loggerCallback = FullCallCounter.createLoggerCallback callCounter + let (openCallback, closeCallback, loggerCallback) = FullCallCounter.createCallbacks callCounter async { use connection = Mocks.NonQuery 3 diff --git a/Vp.FSharp.Sql.Tests/SqlCommand for executeScalar should.fs b/Vp.FSharp.Sql.Tests/SqlCommand for executeScalar should.fs index a2710e0..78f6a04 100644 --- a/Vp.FSharp.Sql.Tests/SqlCommand for executeScalar should.fs +++ b/Vp.FSharp.Sql.Tests/SqlCommand for executeScalar should.fs @@ -63,9 +63,7 @@ let ``leave the connection open if initially open`` () = [] let ``log for all events on globalLogger if connection initially closed`` () = let callCounter = FullCallCounter.initSame 0 - let openCallback = FullCallCounter.createOpenCallback callCounter - let closeCallback = FullCallCounter.createCloseCallback callCounter - let loggerCallback = FullCallCounter.createLoggerCallback callCounter + let (openCallback, closeCallback, loggerCallback) = FullCallCounter.createCallbacks callCounter let data = Mocks.fakeData [[ [16] @@ -92,9 +90,7 @@ let ``log for all events on globalLogger if connection initially closed`` () = [] let ``log for just command events on globalLogger if connection initially not closed`` () = let callCounter = FullCallCounter.initSame 0 - let openCallback = FullCallCounter.createOpenCallback callCounter - let closeCallback = FullCallCounter.createCloseCallback callCounter - let loggerCallback = FullCallCounter.createLoggerCallback callCounter + let (openCallback, closeCallback, loggerCallback) = FullCallCounter.createCallbacks callCounter let data = Mocks.fakeData [[ [17] diff --git a/Vp.FSharp.Sql.Tests/SqlCommand for executeScalarOrNone should.fs b/Vp.FSharp.Sql.Tests/SqlCommand for executeScalarOrNone should.fs index 4772c9c..87aea83 100644 --- a/Vp.FSharp.Sql.Tests/SqlCommand for executeScalarOrNone should.fs +++ b/Vp.FSharp.Sql.Tests/SqlCommand for executeScalarOrNone should.fs @@ -72,9 +72,7 @@ let ``leave the connection open if initially not closed`` () = [] let ``log for all events on globalLogger if connection initially closed`` () = let callCounter = FullCallCounter.initSame 0 - let openCallback = FullCallCounter.createOpenCallback callCounter - let closeCallback = FullCallCounter.createCloseCallback callCounter - let loggerCallback = FullCallCounter.createLoggerCallback callCounter + let (openCallback, closeCallback, loggerCallback) = FullCallCounter.createCallbacks callCounter let data = Mocks.fakeData [[ [16] @@ -102,9 +100,7 @@ let ``log for all events on globalLogger if connection initially closed`` () = [] let ``log for just command events on globalLogger if connection initially not closed`` () = let callCounter = FullCallCounter.initSame 0 - let openCallback = FullCallCounter.createOpenCallback callCounter - let closeCallback = FullCallCounter.createCloseCallback callCounter - let loggerCallback = FullCallCounter.createLoggerCallback callCounter + let (openCallback, closeCallback, loggerCallback) = FullCallCounter.createCallbacks callCounter let data = Mocks.fakeData [[ [17] @@ -183,9 +179,7 @@ let ``leave connection open if initially not closed and retrieve None`` () = [] let ``log for all events on globalLogger if connection initially closed and retrieve None`` () = let callCounter = FullCallCounter.initSame 0 - let openCallback = FullCallCounter.createOpenCallback callCounter - let closeCallback = FullCallCounter.createCloseCallback callCounter - let loggerCallback = FullCallCounter.createLoggerCallback callCounter + let (openCallback, closeCallback, loggerCallback) = FullCallCounter.createCallbacks callCounter let data = Mocks.fakeData [[ [null] @@ -210,9 +204,7 @@ let ``log for all events on globalLogger if connection initially closed and retr [] let ``log for just command events on globalLogger if connection initially not closed and retrieve None`` () = let callCounter = FullCallCounter.initSame 0 - let openCallback = FullCallCounter.createOpenCallback callCounter - let closeCallback = FullCallCounter.createCloseCallback callCounter - let loggerCallback = FullCallCounter.createLoggerCallback callCounter + let (openCallback, closeCallback, loggerCallback) = FullCallCounter.createCallbacks callCounter let data = Mocks.fakeData [[ [null] diff --git a/Vp.FSharp.Sql.Tests/SqlCommand for queryAsyncSeq should.fs b/Vp.FSharp.Sql.Tests/SqlCommand for queryAsyncSeq should.fs index 50f44fe..d98d7bb 100644 --- a/Vp.FSharp.Sql.Tests/SqlCommand for queryAsyncSeq should.fs +++ b/Vp.FSharp.Sql.Tests/SqlCommand for queryAsyncSeq should.fs @@ -229,9 +229,7 @@ let ``leave the connection open if initially not closed with access valueOrNone [] let ``log all events on globalLogger if connection initially closed with access valueOrNone by ordinal`` () = let callCounter = FullCallCounter.initSame 0 - let openCallback = FullCallCounter.createOpenCallback callCounter - let closeCallback = FullCallCounter.createCloseCallback callCounter - let loggerCallback = FullCallCounter.createLoggerCallback callCounter + let (openCallback, closeCallback, loggerCallback) = FullCallCounter.createCallbacks callCounter let data = Mocks.fakeData [[ [1;null;3] @@ -270,9 +268,7 @@ let ``log all events on globalLogger if connection initially closed with access [] let ``log for just command events on globalLogger if connection initially not closed with access valueOrNone by ordinal`` () = let callCounter = FullCallCounter.initSame 0 - let openCallback = FullCallCounter.createOpenCallback callCounter - let closeCallback = FullCallCounter.createCloseCallback callCounter - let loggerCallback = FullCallCounter.createLoggerCallback callCounter + let (openCallback, closeCallback, loggerCallback) = FullCallCounter.createCallbacks callCounter let data = Mocks.fakeData [[ [1;null;3] From 57b9499863395db31ed000247e69a202ce123e1b Mon Sep 17 00:00:00 2001 From: Kerry Perret Date: Thu, 7 Jan 2021 00:21:13 +0100 Subject: [PATCH 3/4] feat(loggers): add sql global conf --- Vp.FSharp.Sql.Tests/Mocks.fs | 46 +++--- .../SqlCommand for executeNonQuery should.fs | 38 ++--- .../SqlCommand for executeScalar should.fs | 44 +++--- ...lCommand for executeScalarOrNone should.fs | 96 +++++++------ .../SqlCommand for queryAsyncSeq should.fs | 133 ++++++++++-------- .../SqlCommand for querySetListN should.fs | 28 ++-- Vp.FSharp.Sql/SqlCommand.fs | 42 +++--- Vp.FSharp.Sql/Types.fs | 15 +- 8 files changed, 246 insertions(+), 196 deletions(-) diff --git a/Vp.FSharp.Sql.Tests/Mocks.fs b/Vp.FSharp.Sql.Tests/Mocks.fs index 2b532b3..b85206c 100644 --- a/Vp.FSharp.Sql.Tests/Mocks.fs +++ b/Vp.FSharp.Sql.Tests/Mocks.fs @@ -43,11 +43,13 @@ let fakeData values columns = | _ -> sprintf "count rows: out of resultSetIndex %i" resultSetIndex |> failwith CountResultSets = List.length values } -let makeDependencies (valToParam: (string -> 'a -> 'b) option) logger = +let makeDeps (valToParam: (string -> 'DbType -> 'DbParameter) option) = { CreateCommand = (fun connection -> connection.CreateCommand()) - ExecuteReaderAsync = (fun cmd -> cmd.ExecuteReaderAsync()) - DbValueToParameter = valToParam |> Option.defaultValue (fun _ _ -> failwith "") - GlobalLogger = logger } + ExecuteReaderAsync = (fun cmd -> cmd.ExecuteReaderAsync) + DbValueToParameter = valToParam |> Option.defaultValue (fun _ _ -> failwith "") } + +let makeGlobalConf logger = + { DefaultLogger = logger } let makeReader data _ = let mutable currentRowIndex = -1 @@ -62,35 +64,35 @@ let makeReader data _ = member this.IsClosed with get() = true member this.RecordsAffected with get() = 0 member this.Item - with get(ordinal: int):Object = null + with get(ordinal: int32):Object = null member this.Item with get(name: string):Object = null - member this.GetDataTypeName (ordinal: int) = + member this.GetDataTypeName (ordinal: int32) = data.Columns.[currentResultSetIndex].[ordinal].NativeTypeName member this.GetEnumerator () = null - member this.GetFieldType (ordinal: int) = + member this.GetFieldType (ordinal: int32) = data.Columns.[currentResultSetIndex].[ordinal].FieldType - member this.GetName (ordinal: int) = + member this.GetName (ordinal: int32) = data.Columns.[currentResultSetIndex].[ordinal].Name member this.GetOrdinal (name: string) = 0 - member this.GetBoolean (ordinal: int) = true - member this.GetByte (ordinal: int) = 0uy + member this.GetBoolean (ordinal: int32) = true + member this.GetByte (ordinal: int32) = 0uy member this.GetBytes (ordinal, dataOffset, buffer, bufferOffset, length) = 0L - member this.GetChar (ordinal: int) = 'a' + member this.GetChar (ordinal: int32) = 'a' member this.GetChars (ordinal, dataOffset, buffer, bufferOffset, length) = 0L - member this.GetDateTime (ordinal: int) = DateTime.Today - member this.GetDecimal (ordinal: int) = 0M - member this.GetDouble (ordinal: int) = 0.0 - member this.GetFloat (ordinal: int) = 0F - member this.GetGuid (ordinal: int) = Guid.Empty - member this.GetInt16 (ordinal: int) = 0s - member this.GetInt32 (ordinal: int) = 0 - member this.GetInt64 (ordinal: int) = 0L - member this.GetString (ordinal: int) = "" - member this.GetValue (ordinal: int) = + member this.GetDateTime (ordinal: int32) = DateTime.Today + member this.GetDecimal (ordinal: int32) = 0M + member this.GetDouble (ordinal: int32) = 0.0 + member this.GetFloat (ordinal: int32) = 0F + member this.GetGuid (ordinal: int32) = Guid.Empty + member this.GetInt16 (ordinal: int32) = 0s + member this.GetInt32 (ordinal: int32) = 0 + member this.GetInt64 (ordinal: int32) = 0L + member this.GetString (ordinal: int32) = "" + member this.GetValue (ordinal: int32) = (data.GetValues currentResultSetIndex currentRowIndex).[ordinal] member this.GetValues values = 0 - member this.IsDBNull (ordinal: int) = + member this.IsDBNull (ordinal: int32) = (data.GetValues currentResultSetIndex currentRowIndex).[ordinal] = null member this.NextResult () = currentResultSetIndex <- currentResultSetIndex + 1 diff --git a/Vp.FSharp.Sql.Tests/SqlCommand for executeNonQuery should.fs b/Vp.FSharp.Sql.Tests/SqlCommand for executeNonQuery should.fs index a2c77c7..445a593 100644 --- a/Vp.FSharp.Sql.Tests/SqlCommand for executeNonQuery should.fs +++ b/Vp.FSharp.Sql.Tests/SqlCommand for executeNonQuery should.fs @@ -18,11 +18,13 @@ let ``open and then close the connection if initially closed`` () = use connection = Mocks.NonQuery 0 |> Mocks.makeConnection "toto" ConnectionState.Closed openCallback closeCallback - let! r = + let deps = Mocks.makeDeps None + let globalConf = Mocks.makeGlobalConf None + let! outcome = SqlCommand.text "update" |> SqlCommand.noLogger - |> SqlCommand.executeNonQuery connection (Mocks.makeDependencies None None) - r =! 0 + |> SqlCommand.executeNonQuery connection deps globalConf + outcome =! 0 PartialCallCounter.assertEqual callCounter 1 1 } @@ -34,11 +36,13 @@ let ``leave the connection open if initially open`` () = use connection = Mocks.NonQuery 1 |> Mocks.makeConnection "toto" ConnectionState.Connecting openCallback closeCallback - let! r = + let deps = Mocks.makeDeps None + let globalConf = Mocks.makeGlobalConf None + let! outcome = SqlCommand.text "update" |> SqlCommand.noLogger - |> SqlCommand.executeNonQuery connection (Mocks.makeDependencies None None) - r =! 1 + |> SqlCommand.executeNonQuery connection deps globalConf + outcome =! 1 PartialCallCounter.assertEqual callCounter 0 0 } @@ -50,13 +54,12 @@ let ``log for all events on globalLogger if connection initially closed`` () = use connection = Mocks.NonQuery 2 |> Mocks.makeConnection "toto" ConnectionState.Closed openCallback closeCallback - let deps = - Some loggerCallback - |> Mocks.makeDependencies None - let! r = + let deps = Mocks.makeDeps None + let globalConf = Mocks.makeGlobalConf (Some loggerCallback) + let! outcome = SqlCommand.text "update" - |> SqlCommand.executeNonQuery connection deps - r =! 2 + |> SqlCommand.executeNonQuery connection deps globalConf + outcome =! 2 FullCallCounter.assertEqual callCounter 1 1 1 1 1 1 } @@ -68,12 +71,11 @@ let ``log just command events on globalLogger if connection initially not closed use connection = Mocks.NonQuery 3 |> Mocks.makeConnection "toto" ConnectionState.Connecting openCallback closeCallback - let deps = - Some loggerCallback - |> Mocks.makeDependencies None - let! r = + let deps = Mocks.makeDeps None + let globalConf = Mocks.makeGlobalConf (Some loggerCallback) + let! outcome = SqlCommand.text "update" - |> SqlCommand.executeNonQuery connection deps - r =! 3 + |> SqlCommand.executeNonQuery connection deps globalConf + outcome =! 3 FullCallCounter.assertEqual callCounter 0 0 0 0 1 1 } diff --git a/Vp.FSharp.Sql.Tests/SqlCommand for executeScalar should.fs b/Vp.FSharp.Sql.Tests/SqlCommand for executeScalar should.fs index 78f6a04..1e6a03e 100644 --- a/Vp.FSharp.Sql.Tests/SqlCommand for executeScalar should.fs +++ b/Vp.FSharp.Sql.Tests/SqlCommand for executeScalar should.fs @@ -28,10 +28,13 @@ let ``open and then close the connection if initially closed`` () = use connection = Mocks.Reader (Mocks.makeReader data) |> Mocks.makeConnection "toto" ConnectionState.Closed openCallback closeCallback - let! r = SqlCommand.text "select 1" - |> SqlCommand.noLogger - |> SqlCommand.executeScalar connection (Mocks.makeDependencies None None) - r =! 14 + let deps = Mocks.makeDeps None + let globalConf = Mocks.makeGlobalConf None + let! outcome = + SqlCommand.text "select 1" + |> SqlCommand.noLogger + |> SqlCommand.executeScalar connection deps globalConf + outcome =! 14 PartialCallCounter.assertEqual callCounter 1 1 } @@ -53,10 +56,13 @@ let ``leave the connection open if initially open`` () = use connection = Mocks.Reader (Mocks.makeReader data) |> Mocks.makeConnection "toto" ConnectionState.Connecting openCallback closeCallback - let! r = SqlCommand.text "select 1" - |> SqlCommand.noLogger - |> SqlCommand.executeScalar connection (Mocks.makeDependencies None None) - r =! 15 + let deps = Mocks.makeDeps None + let globalConf = Mocks.makeGlobalConf None + let! outcome = + SqlCommand.text "select 1" + |> SqlCommand.noLogger + |> SqlCommand.executeScalar connection deps globalConf + outcome =! 15 PartialCallCounter.assertEqual callCounter 0 0 } @@ -77,13 +83,12 @@ let ``log for all events on globalLogger if connection initially closed`` () = use connection = Mocks.Reader (Mocks.makeReader data) |> Mocks.makeConnection "toto" ConnectionState.Closed openCallback closeCallback - let deps = - Some loggerCallback - |> Mocks.makeDependencies None - let! r = + let deps = Mocks.makeDeps None + let globalConf = Mocks.makeGlobalConf (Some loggerCallback) + let! outcome = SqlCommand.text "select 1" - |> SqlCommand.executeScalar connection deps - r =! 16 + |> SqlCommand.executeScalar connection deps globalConf + outcome =! 16 FullCallCounter.assertEqual callCounter 1 1 1 1 1 1 } @@ -104,12 +109,11 @@ let ``log for just command events on globalLogger if connection initially not cl use connection = Mocks.Reader (Mocks.makeReader data) |> Mocks.makeConnection "toto" ConnectionState.Connecting openCallback closeCallback - let deps = - Some loggerCallback - |> Mocks.makeDependencies None - let! r = + let deps = Mocks.makeDeps None + let globalConf = Mocks.makeGlobalConf (Some loggerCallback) + let! outcome = SqlCommand.text "select 1" - |> SqlCommand.executeScalar connection deps - r =! 17 + |> SqlCommand.executeScalar connection deps globalConf + outcome =! 17 FullCallCounter.assertEqual callCounter 0 0 0 0 1 1 } diff --git a/Vp.FSharp.Sql.Tests/SqlCommand for executeScalarOrNone should.fs b/Vp.FSharp.Sql.Tests/SqlCommand for executeScalarOrNone should.fs index 87aea83..92bff9f 100644 --- a/Vp.FSharp.Sql.Tests/SqlCommand for executeScalarOrNone should.fs +++ b/Vp.FSharp.Sql.Tests/SqlCommand for executeScalarOrNone should.fs @@ -29,11 +29,14 @@ let ``open and then close the connection if initially closed`` () = use connection = Mocks.Reader (Mocks.makeReader data) |> Mocks.makeConnection "toto" ConnectionState.Closed openCallback closeCallback - let! r = SqlCommand.text "select 1" - |> SqlCommand.noLogger - |> SqlCommand.executeScalarOrNone connection (Mocks.makeDependencies None None) - r.IsSome =! true - r + let deps = Mocks.makeDeps None + let globalConf = Mocks.makeGlobalConf None + let! outcome = + SqlCommand.text "select 1" + |> SqlCommand.noLogger + |> SqlCommand.executeScalarOrNone connection deps globalConf + outcome.IsSome =! true + outcome |> Option.defaultValue 42 |> (=!) 14 PartialCallCounter.assertEqual callCounter 1 1 @@ -58,11 +61,14 @@ let ``leave the connection open if initially not closed`` () = use connection = Mocks.Reader (Mocks.makeReader data) |> Mocks.makeConnection "toto" ConnectionState.Connecting openCallback closeCallback - let! r = SqlCommand.text "select 1" - |> SqlCommand.noLogger - |> SqlCommand.executeScalarOrNone connection (Mocks.makeDependencies None None) - r.IsSome =! true - r + let deps = Mocks.makeDeps None + let globalConf = Mocks.makeGlobalConf None + let! outcome = + SqlCommand.text "select 1" + |> SqlCommand.noLogger + |> SqlCommand.executeScalarOrNone connection deps globalConf + outcome.IsSome =! true + outcome |> Option.defaultValue 42 |> (=!) 15 @@ -86,12 +92,13 @@ let ``log for all events on globalLogger if connection initially closed`` () = use connection = Mocks.Reader (Mocks.makeReader data) |> Mocks.makeConnection "toto" ConnectionState.Closed openCallback closeCallback - let deps = Some loggerCallback - |> Mocks.makeDependencies None - let! r = SqlCommand.text "select 1" - |> SqlCommand.executeScalarOrNone connection deps - r.IsSome =! true - r + let deps = Mocks.makeDeps None + let globalConf = Mocks.makeGlobalConf (Some loggerCallback) + let! outcome = + SqlCommand.text "select 1" + |> SqlCommand.executeScalarOrNone connection deps globalConf + outcome.IsSome =! true + outcome |> Option.defaultValue 42 |> (=!) 16 FullCallCounter.assertEqual callCounter 1 1 1 1 1 1 @@ -114,12 +121,13 @@ let ``log for just command events on globalLogger if connection initially not cl use connection = Mocks.Reader (Mocks.makeReader data) |> Mocks.makeConnection "toto" ConnectionState.Connecting openCallback closeCallback - let deps = Some loggerCallback - |> Mocks.makeDependencies None - let! r = SqlCommand.text "select 1" - |> SqlCommand.executeScalarOrNone connection deps - r.IsSome =! true - r + let deps = Mocks.makeDeps None + let globalConf = Mocks.makeGlobalConf (Some loggerCallback) + let! outcome = + SqlCommand.text "select 1" + |> SqlCommand.executeScalarOrNone connection deps globalConf + outcome.IsSome =! true + outcome |> Option.defaultValue 42 |> (=!) 17 FullCallCounter.assertEqual callCounter 0 0 0 0 1 1 @@ -144,10 +152,13 @@ let ``open and then close connection if initially closed and retrieve None`` () use connection = Mocks.Reader (Mocks.makeReader data) |> Mocks.makeConnection "toto" ConnectionState.Closed openCallback closeCallback - let! r = SqlCommand.text "select 1" - |> SqlCommand.noLogger - |> SqlCommand.executeScalarOrNone connection (Mocks.makeDependencies None None) - r.IsNone =! true + let deps = Mocks.makeDeps None + let globalConf = Mocks.makeGlobalConf None + let! outcome = + SqlCommand.text "select 1" + |> SqlCommand.noLogger + |> SqlCommand.executeScalarOrNone connection deps globalConf + outcome.IsNone =! true PartialCallCounter.assertEqual callCounter 1 1 } @@ -169,10 +180,13 @@ let ``leave connection open if initially not closed and retrieve None`` () = use connection = Mocks.Reader (Mocks.makeReader data) |> Mocks.makeConnection "toto" ConnectionState.Connecting openCallback closeCallback - let! r = SqlCommand.text "select 1" - |> SqlCommand.noLogger - |> SqlCommand.executeScalarOrNone connection (Mocks.makeDependencies None None) - r.IsNone =! true + let deps = Mocks.makeDeps None + let globalConf = Mocks.makeGlobalConf None + let! outcome = + SqlCommand.text "select 1" + |> SqlCommand.noLogger + |> SqlCommand.executeScalarOrNone connection deps globalConf + outcome.IsNone =! true PartialCallCounter.assertEqual callCounter 0 0 } @@ -193,11 +207,12 @@ let ``log for all events on globalLogger if connection initially closed and retr use connection = Mocks.Reader (Mocks.makeReader data) |> Mocks.makeConnection "toto" ConnectionState.Closed openCallback closeCallback - let deps = Some loggerCallback - |> Mocks.makeDependencies None - let! r = SqlCommand.text "select 1" - |> SqlCommand.executeScalarOrNone connection deps - r.IsNone =! true + let deps = Mocks.makeDeps None + let globalConf = Mocks.makeGlobalConf (Some loggerCallback) + let! outcome = + SqlCommand.text "select 1" + |> SqlCommand.executeScalarOrNone connection deps globalConf + outcome.IsNone =! true FullCallCounter.assertEqual callCounter 1 1 1 1 1 1 } @@ -218,10 +233,11 @@ let ``log for just command events on globalLogger if connection initially not cl use connection = Mocks.Reader (Mocks.makeReader data) |> Mocks.makeConnection "toto" ConnectionState.Connecting openCallback closeCallback - let deps = Some loggerCallback - |> Mocks.makeDependencies None - let! r = SqlCommand.text "select 1" - |> SqlCommand.executeScalarOrNone connection deps - r.IsNone =! true + let deps = Mocks.makeDeps None + let globalConf = Mocks.makeGlobalConf (Some loggerCallback) + let! outcome = + SqlCommand.text "select 1" + |> SqlCommand.executeScalarOrNone connection deps globalConf + outcome.IsNone =! true FullCallCounter.assertEqual callCounter 0 0 0 0 1 1 } diff --git a/Vp.FSharp.Sql.Tests/SqlCommand for queryAsyncSeq should.fs b/Vp.FSharp.Sql.Tests/SqlCommand for queryAsyncSeq should.fs index d98d7bb..8f71163 100644 --- a/Vp.FSharp.Sql.Tests/SqlCommand for queryAsyncSeq should.fs +++ b/Vp.FSharp.Sql.Tests/SqlCommand for queryAsyncSeq should.fs @@ -63,14 +63,17 @@ let ``open and then close the connection if initially closed and access value by use connection = Mocks.Reader (Mocks.makeReader data) |> Mocks.makeConnection "toto" ConnectionState.Closed openCallback closeCallback - let r = SqlCommand.text "select 1" - |> SqlCommand.noLogger - |> SqlCommand.queryAsyncSeq connection (Mocks.makeDependencies None None) - (readValueByFieldName ([2;1;0] |> toFieldNames)) - |> AsyncSeq.toListSynchronously - |> List.sortBy (fun values -> snd values.[0]) - r.Length =! 2 - r =! [[("id2", 3);("id1", 2);("id0", 1)];[("id2", 6);("id1", 5);("id0", 4)]] + let deps = Mocks.makeDeps None + let globalConf = Mocks.makeGlobalConf None + let outcome = + SqlCommand.text "select 1" + |> SqlCommand.noLogger + |> SqlCommand.queryAsyncSeq connection deps globalConf + (readValueByFieldName ([2;1;0] |> toFieldNames)) + |> AsyncSeq.toListSynchronously + |> List.sortBy (fun values -> snd values.[0]) + outcome.Length =! 2 + outcome =! [[("id2", 3);("id1", 2);("id0", 1)];[("id2", 6);("id1", 5);("id0", 4)]] PartialCallCounter.assertEqual callCounter 1 1 } @@ -101,14 +104,17 @@ let ``open and then close the connection if initially closed and access value by use connection = Mocks.Reader (Mocks.makeReader data) |> Mocks.makeConnection "toto" ConnectionState.Closed openCallback closeCallback - let r = SqlCommand.text "select 1" - |> SqlCommand.noLogger - |> SqlCommand.queryAsyncSeq connection (Mocks.makeDependencies None None) - (readValueByIndex [2;1;0]) - |> AsyncSeq.toListSynchronously - |> List.sortBy (fun values -> values.[0]) - r.Length =! 2 - r =! [[3;2;1];[6;5;4]] + let deps = Mocks.makeDeps None + let globalConf = Mocks.makeGlobalConf None + let outcome = + SqlCommand.text "select 1" + |> SqlCommand.noLogger + |> SqlCommand.queryAsyncSeq connection deps globalConf + (readValueByIndex [2;1;0]) + |> AsyncSeq.toListSynchronously + |> List.sortBy (fun values -> values.[0]) + outcome.Length =! 2 + outcome =! [[3;2;1];[6;5;4]] PartialCallCounter.assertEqual callCounter 1 1 } @@ -139,14 +145,17 @@ let ``open and then close the connection if initially closed and access valueOrN use connection = Mocks.Reader (Mocks.makeReader data) |> Mocks.makeConnection "toto" ConnectionState.Closed openCallback closeCallback - let r = SqlCommand.text "select 1" - |> SqlCommand.noLogger - |> SqlCommand.queryAsyncSeq connection (Mocks.makeDependencies None None) - (readValueOrNoneByFieldName (toFieldNames [2;1;0])) - |> AsyncSeq.toListSynchronously - |> List.sortBy (fun values -> snd values.[0]) - r.Length =! 2 - r =! [[("id2", Some 3);("id1", None);("id0", Some 1)];[("id2", Some 6);("id1", Some 5);("id0", Some 4)]] + let deps = Mocks.makeDeps None + let globalConf = Mocks.makeGlobalConf None + let outcome = + SqlCommand.text "select 1" + |> SqlCommand.noLogger + |> SqlCommand.queryAsyncSeq connection deps globalConf + (readValueOrNoneByFieldName (toFieldNames [2;1;0])) + |> AsyncSeq.toListSynchronously + |> List.sortBy (fun values -> snd values.[0]) + outcome.Length =! 2 + outcome =! [[("id2", Some 3);("id1", None);("id0", Some 1)];[("id2", Some 6);("id1", Some 5);("id0", Some 4)]] PartialCallCounter.assertEqual callCounter 1 1 } @@ -177,14 +186,17 @@ let ``open and then close connection if initially closed and access valueOrNone use connection = Mocks.Reader (Mocks.makeReader data) |> Mocks.makeConnection "toto" ConnectionState.Closed openCallback closeCallback - let r = SqlCommand.text "select 1" - |> SqlCommand.noLogger - |> SqlCommand.queryAsyncSeq connection (Mocks.makeDependencies None None) - (readValueOrNoneByIndex [2;1;0]) - |> AsyncSeq.toListSynchronously - |> List.sortBy (fun values -> values.[0]) - r.Length =! 2 - r =! [[Some 3; None; Some 1];[Some 6; Some 5; Some 4]] + let deps = Mocks.makeDeps None + let globalConf = Mocks.makeGlobalConf None + let outcome = + SqlCommand.text "select 1" + |> SqlCommand.noLogger + |> SqlCommand.queryAsyncSeq connection deps globalConf + (readValueOrNoneByIndex [2;1;0]) + |> AsyncSeq.toListSynchronously + |> List.sortBy (fun values -> values.[0]) + outcome.Length =! 2 + outcome =! [[Some 3; None; Some 1];[Some 6; Some 5; Some 4]] PartialCallCounter.assertEqual callCounter 1 1 } @@ -215,14 +227,17 @@ let ``leave the connection open if initially not closed with access valueOrNone use connection = Mocks.Reader (Mocks.makeReader data) |> Mocks.makeConnection "toto" ConnectionState.Connecting openCallback closeCallback - let r = SqlCommand.text "select 1" - |> SqlCommand.noLogger - |> SqlCommand.queryAsyncSeq connection (Mocks.makeDependencies None None) - (readValueOrNoneByIndex [2;1;0]) - |> AsyncSeq.toListSynchronously - |> List.sortBy (fun values -> values.[0]) - r.Length =! 2 - r =! [[Some 3; None; Some 1];[Some 6; Some 5; Some 4]] + let deps = Mocks.makeDeps None + let globalConf = Mocks.makeGlobalConf None + let outcome = + SqlCommand.text "select 1" + |> SqlCommand.noLogger + |> SqlCommand.queryAsyncSeq connection deps globalConf + (readValueOrNoneByIndex [2;1;0]) + |> AsyncSeq.toListSynchronously + |> List.sortBy (fun values -> values.[0]) + outcome.Length =! 2 + outcome =! [[Some 3; None; Some 1];[Some 6; Some 5; Some 4]] PartialCallCounter.assertEqual callCounter 0 0 } @@ -253,15 +268,16 @@ let ``log all events on globalLogger if connection initially closed with access use connection = Mocks.Reader (Mocks.makeReader data) |> Mocks.makeConnection "toto" ConnectionState.Closed openCallback closeCallback - let deps = Some loggerCallback - |> Mocks.makeDependencies None - let r = SqlCommand.text "select 1" - |> SqlCommand.queryAsyncSeq connection deps - (readValueOrNoneByIndex [2;1;0]) - |> AsyncSeq.toListSynchronously - |> List.sortBy (fun values -> values.[0]) - r.Length =! 2 - r =! [[Some 3; None; Some 1];[Some 6; Some 5; Some 4]] + let deps = Mocks.makeDeps None + let globalConf = Mocks.makeGlobalConf (Some loggerCallback) + let outcome = + SqlCommand.text "select 1" + |> SqlCommand.queryAsyncSeq connection deps globalConf + (readValueOrNoneByIndex [2;1;0]) + |> AsyncSeq.toListSynchronously + |> List.sortBy (fun values -> values.[0]) + outcome.Length =! 2 + outcome =! [[Some 3; None; Some 1];[Some 6; Some 5; Some 4]] FullCallCounter.assertEqual callCounter 1 1 1 1 1 1 } @@ -292,14 +308,15 @@ let ``log for just command events on globalLogger if connection initially not cl use connection = Mocks.Reader (Mocks.makeReader data) |> Mocks.makeConnection "toto" ConnectionState.Connecting openCallback closeCallback - let deps = Some loggerCallback - |> Mocks.makeDependencies None - let r = SqlCommand.text "select 1" - |> SqlCommand.queryAsyncSeq connection deps - (readValueOrNoneByIndex [2;1;0]) - |> AsyncSeq.toListSynchronously - |> List.sortBy (fun values -> values.[0]) - r.Length =! 2 - r =! [[Some 3; None; Some 1];[Some 6; Some 5; Some 4]] + let deps = Mocks.makeDeps None + let globalConf = Mocks.makeGlobalConf (Some loggerCallback) + let outcome = + SqlCommand.text "select 1" + |> SqlCommand.queryAsyncSeq connection deps globalConf + (readValueOrNoneByIndex [2;1;0]) + |> AsyncSeq.toListSynchronously + |> List.sortBy (fun values -> values.[0]) + outcome.Length =! 2 + outcome =! [[Some 3; None; Some 1];[Some 6; Some 5; Some 4]] FullCallCounter.assertEqual callCounter 0 0 0 0 1 1 } diff --git a/Vp.FSharp.Sql.Tests/SqlCommand for querySetListN should.fs b/Vp.FSharp.Sql.Tests/SqlCommand for querySetListN should.fs index 6d2b20c..02cbcdd 100644 --- a/Vp.FSharp.Sql.Tests/SqlCommand for querySetListN should.fs +++ b/Vp.FSharp.Sql.Tests/SqlCommand for querySetListN should.fs @@ -40,12 +40,14 @@ let ``have querySetList should open and then close the connection if initially c use connection = Mocks.Reader (Mocks.makeReader data) |> Mocks.makeConnection "toto" ConnectionState.Closed openCallback closeCallback + let deps = Mocks.makeDeps None + let globalConf = Mocks.makeGlobalConf None let! r0 = - SqlCommand.text "select 1" - |> SqlCommand.noLogger - |> SqlCommand.querySetList connection - (Mocks.makeDependencies None None) - (readValueByIndex columnsIndex 0) + SqlCommand.text "select 1" + |> SqlCommand.noLogger + |> SqlCommand.querySetList connection + deps globalConf + (readValueByIndex columnsIndex 0) r0.Length =! 2 r0 =! ([1..6] |> List.splitInto 2) PartialCallCounter.assertEqual callCounter 1 1 @@ -65,12 +67,14 @@ let ``have querySetList2 should open and then close the connection if initially use connection = Mocks.Reader (Mocks.makeReader data) |> Mocks.makeConnection "toto" ConnectionState.Closed openCallback closeCallback + let deps = Mocks.makeDeps None + let globalConf = Mocks.makeGlobalConf None let! (r0, r1) = - SqlCommand.text "select 1" - |> SqlCommand.noLogger - |> SqlCommand.querySetList2 connection (Mocks.makeDependencies None None) - (readValueByIndex columnsIndex 0) - (readValueByIndex columnsIndex 1) + SqlCommand.text "select 1" + |> SqlCommand.noLogger + |> SqlCommand.querySetList2 connection deps globalConf + (readValueByIndex columnsIndex 0) + (readValueByIndex columnsIndex 1) r0.Length =! 2 r0 =! ([1..6] |> List.splitInto 2) r1.Length =! 2 @@ -94,10 +98,12 @@ let ``have querySetList3 open and then close the connection if initially closed use connection = Mocks.Reader (Mocks.makeReader data) |> Mocks.makeConnection "toto" ConnectionState.Closed openCallback closeCallback + let deps = Mocks.makeDeps None + let globalConf = Mocks.makeGlobalConf None let! (r0, r1, r2) = SqlCommand.text "select 1" |> SqlCommand.noLogger - |> SqlCommand.querySetList3 connection (Mocks.makeDependencies None None) + |> SqlCommand.querySetList3 connection deps globalConf (readValueByIndex columnsIndex 0) (readValueByIndex columnsIndex 1) (readValueByIndex columnsIndex 2) diff --git a/Vp.FSharp.Sql/SqlCommand.fs b/Vp.FSharp.Sql/SqlCommand.fs index aeac001..0e858e7 100644 --- a/Vp.FSharp.Sql/SqlCommand.fs +++ b/Vp.FSharp.Sql/SqlCommand.fs @@ -97,9 +97,9 @@ module Vp.FSharp.Sql.SqlCommand connection.OpenAsync(cancellationToken) |> Async.AwaitTask - let private log4 deps commandDefinition sqlLog = + let private log4 globalConf commandDefinition sqlLog = match commandDefinition.Logger with - | Global -> deps.GlobalLogger + | Global -> globalConf.DefaultLogger | Override logging -> Some logging | Nothing -> None |> Option.iter (fun f -> f sqlLog) @@ -127,11 +127,11 @@ module Vp.FSharp.Sql.SqlCommand } /// Return the sets of rows as an AsyncSeq accordingly to the command definition. - let queryAsyncSeq (connection: #DbConnection) deps read commandDefinition = + let queryAsyncSeq (connection: #DbConnection) deps globalConf read commandDefinition = asyncSeq { let! linkedToken = Async.linkedTokenSourceFrom commandDefinition.CancellationToken let wasClosed = connection.State = ConnectionState.Closed - let log sqlLog = log4 deps commandDefinition sqlLog + let log sqlLog = log4 globalConf commandDefinition sqlLog let connectionStopwatch = Stopwatch() let commandStopwatch = Stopwatch() use! command = setupCommand deps commandDefinition linkedToken connection @@ -144,7 +144,7 @@ module Vp.FSharp.Sql.SqlCommand CommandPrepared command |> log commandStopwatch.Start () - use! dbDataReader = command.ExecuteReaderAsync(linkedToken) |> Async.AwaitTask + use! dbDataReader = deps.ExecuteReaderAsync command linkedToken |> Async.AwaitTask let items = AsyncSeq.initInfinite(fun _ -> (dbDataReader, linkedToken)) |> SkipFirstAsyncSeq.scanAsync( @@ -168,23 +168,23 @@ module Vp.FSharp.Sql.SqlCommand } /// Return the sets of rows as a list accordingly to the command definition. - let queryList connection dbValueToParameter read commandDefinition = - queryAsyncSeq connection dbValueToParameter read commandDefinition + let queryList connection deps globalConf read commandDefinition = + queryAsyncSeq connection deps globalConf read commandDefinition |> AsyncSeq.toListAsync /// Return the first set of rows as a list accordingly to the command definition. - let querySetList (connection: #DbConnection) deps read commandDefinition = + let querySetList (connection: #DbConnection) deps globalConf read commandDefinition = async { let setList = ResizeArray() let readRecord setIndex recordIndex recordReader = if setIndex = 0 then setList.Add(read recordIndex recordReader) else () - do! queryAsyncSeq connection deps readRecord commandDefinition |> AsyncSeq.consume + do! queryAsyncSeq connection deps globalConf readRecord commandDefinition |> AsyncSeq.consume return setList |> Seq.toList } /// Return the 2 first sets of rows as a tuple of 2 lists accordingly to the command definition. - let querySetList2 connection deps read1 read2 commandDefinition = + let querySetList2 connection deps globalConf read1 read2 commandDefinition = async { let set1List = ResizeArray() let set2List = ResizeArray() @@ -192,12 +192,12 @@ module Vp.FSharp.Sql.SqlCommand if setIndex = 0 then set1List.Add(read1 recordIndex recordReader) elif setIndex = 1 then set2List.Add(read2 recordIndex recordReader) else () - do! queryAsyncSeq connection deps readRecord commandDefinition |> AsyncSeq.consume + do! queryAsyncSeq connection deps globalConf readRecord commandDefinition |> AsyncSeq.consume return (set1List |> Seq.toList, set2List |> Seq.toList) } /// Return the 3 first sets of rows as a tuple of 3 lists accordingly to the command definition. - let querySetList3 connection deps read1 read2 read3 commandDefinition = + let querySetList3 connection deps globalConf read1 read2 read3 commandDefinition = async { let set1List = ResizeArray() let set2List = ResizeArray() @@ -207,18 +207,18 @@ module Vp.FSharp.Sql.SqlCommand elif setIndex = 1 then set2List.Add(read2 recordIndex recordReader) elif setIndex = 2 then set3List.Add(read3 recordIndex recordReader) else () - do! queryAsyncSeq connection deps readRecord commandDefinition |> AsyncSeq.consume + do! queryAsyncSeq connection deps globalConf readRecord commandDefinition |> AsyncSeq.consume return (set1List |> Seq.toList, set2List |> Seq.toList, set3List |> Seq.toList) } /// Execute the command accordingly to its definition and, /// - return the first cell value, if it is available and of the given type. /// - throw an exception, otherwise. - let executeScalar<'Scalar, .. > (connection: #DbConnection) deps commandDefinition = + let executeScalar<'Scalar, .. > (connection: #DbConnection) deps globalConf commandDefinition = async { let! linkedToken = Async.linkedTokenSourceFrom commandDefinition.CancellationToken let wasClosed = connection.State = ConnectionState.Closed - let log sqlLog = log4 deps commandDefinition sqlLog + let log sqlLog = log4 globalConf commandDefinition sqlLog let connectionStopwatch = Stopwatch() let commandStopwatch = Stopwatch() use! command = setupCommand deps commandDefinition linkedToken connection @@ -231,7 +231,7 @@ module Vp.FSharp.Sql.SqlCommand CommandPrepared command |> log commandStopwatch.Start () - use! dataReader = command.ExecuteReaderAsync(linkedToken) |> Async.AwaitTask + use! dataReader = deps.ExecuteReaderAsync command linkedToken |> Async.AwaitTask let! anyData = dataReader.ReadAsync(linkedToken) |> Async.AwaitTask if not anyData then return raise SqlNoDataAvailableException @@ -254,12 +254,12 @@ module Vp.FSharp.Sql.SqlCommand /// - return Some, if the first cell is available and of the given type. /// - return None, if first cell is DbNull. /// - throw an exception, otherwise. - let executeScalarOrNone<'Scalar, .. > (connection: #DbConnection) deps commandDefinition = + let executeScalarOrNone<'Scalar, .. > (connection: #DbConnection) deps globalConf commandDefinition = async { let! linkedToken = Async.linkedTokenSourceFrom commandDefinition.CancellationToken let wasClosed = connection.State = ConnectionState.Closed - let log sqlLog = log4 deps commandDefinition sqlLog + let log sqlLog = log4 globalConf commandDefinition sqlLog let connectionStopwatch = Stopwatch() let commandStopwatch = Stopwatch() use! command = setupCommand deps commandDefinition linkedToken connection @@ -272,7 +272,7 @@ module Vp.FSharp.Sql.SqlCommand CommandPrepared command |> log commandStopwatch.Start () - use! dataReader = command.ExecuteReaderAsync(linkedToken) |> Async.AwaitTask + use! dataReader = deps.ExecuteReaderAsync command linkedToken |> Async.AwaitTask let! anyData = dataReader.ReadAsync(linkedToken) |> Async.AwaitTask if not anyData then return raise SqlNoDataAvailableException @@ -289,11 +289,11 @@ module Vp.FSharp.Sql.SqlCommand } /// Execute the command accordingly to its definition and, return the number of rows affected. - let executeNonQuery (connection: #DbConnection) deps commandDefinition = + let executeNonQuery (connection: #DbConnection) deps globalConf commandDefinition = async { let! linkedToken = Async.linkedTokenSourceFrom commandDefinition.CancellationToken let wasClosed = connection.State = ConnectionState.Closed - let log sqlLog = log4 deps commandDefinition sqlLog + let log sqlLog = log4 globalConf commandDefinition sqlLog let connectionStopwatch = Stopwatch() let commandStopwatch = Stopwatch() use! command = setupCommand deps commandDefinition linkedToken connection diff --git a/Vp.FSharp.Sql/Types.fs b/Vp.FSharp.Sql/Types.fs index a64a557..9a2eca3 100644 --- a/Vp.FSharp.Sql/Types.fs +++ b/Vp.FSharp.Sql/Types.fs @@ -43,17 +43,20 @@ type CommandDefinition<'DbConnection, 'DbTransaction, 'DbCommand, 'DbParameter, Transaction: 'DbTransaction option Logger: LoggerKind<'DbConnection, 'DbCommand> } +type SqlGlobalConf<'DbConnection, 'DbCommand + when 'DbConnection :> DbConnection + and 'DbCommand :> DbCommand> = + { DefaultLogger: (SqlLog<'DbConnection, 'DbCommand> -> unit) option } + type SqlDeps<'DbConnection, 'DbTransaction, 'DbCommand, 'DbParameter, 'DbDataReader, 'DbType when 'DbConnection :> DbConnection and 'DbTransaction :> DbTransaction and 'DbCommand :> DbCommand and 'DbParameter :> DbParameter - and 'DbDataReader :> DbDataReader> = { - CreateCommand: 'DbConnection -> 'DbCommand - ExecuteReaderAsync: 'DbCommand -> Task<'DbDataReader> - DbValueToParameter: string -> 'DbType -> 'DbParameter - GlobalLogger: (SqlLog<'DbConnection, 'DbCommand> -> unit) option - } + and 'DbDataReader :> DbDataReader> = + { CreateCommand: 'DbConnection -> 'DbCommand + ExecuteReaderAsync: 'DbCommand -> CancellationToken -> Task<'DbDataReader> + DbValueToParameter: string -> 'DbType -> 'DbParameter } type DbField = { Name: string From cc49cb6d98fc05de00e6eee160a8bcd3eb972863 Mon Sep 17 00:00:00 2001 From: Kerry Perret Date: Thu, 7 Jan 2021 02:48:55 +0100 Subject: [PATCH 4/4] feat(conf): ref conf --- Vp.FSharp.Sql.Tests/Mocks.fs | 2 +- .../SqlCommand for executeNonQuery should.fs | 16 ++++---- .../SqlCommand for executeScalar should.fs | 16 ++++---- ...lCommand for executeScalarOrNone should.fs | 32 ++++++++-------- .../SqlCommand for queryAsyncSeq should.fs | 28 +++++++------- .../SqlCommand for querySetListN should.fs | 12 +++--- Vp.FSharp.Sql/SqlCommand.fs | 38 +++++++++---------- Vp.FSharp.Sql/Types.fs | 27 +++++++++++-- 8 files changed, 95 insertions(+), 76 deletions(-) diff --git a/Vp.FSharp.Sql.Tests/Mocks.fs b/Vp.FSharp.Sql.Tests/Mocks.fs index b85206c..1b777e1 100644 --- a/Vp.FSharp.Sql.Tests/Mocks.fs +++ b/Vp.FSharp.Sql.Tests/Mocks.fs @@ -48,7 +48,7 @@ let makeDeps (valToParam: (string -> 'DbType -> 'DbParameter) option) = ExecuteReaderAsync = (fun cmd -> cmd.ExecuteReaderAsync) DbValueToParameter = valToParam |> Option.defaultValue (fun _ _ -> failwith "") } -let makeGlobalConf logger = +let makeConf logger = { DefaultLogger = logger } let makeReader data _ = diff --git a/Vp.FSharp.Sql.Tests/SqlCommand for executeNonQuery should.fs b/Vp.FSharp.Sql.Tests/SqlCommand for executeNonQuery should.fs index 445a593..324fd4d 100644 --- a/Vp.FSharp.Sql.Tests/SqlCommand for executeNonQuery should.fs +++ b/Vp.FSharp.Sql.Tests/SqlCommand for executeNonQuery should.fs @@ -19,11 +19,11 @@ let ``open and then close the connection if initially closed`` () = Mocks.NonQuery 0 |> Mocks.makeConnection "toto" ConnectionState.Closed openCallback closeCallback let deps = Mocks.makeDeps None - let globalConf = Mocks.makeGlobalConf None + let conf = Mocks.makeConf None let! outcome = SqlCommand.text "update" |> SqlCommand.noLogger - |> SqlCommand.executeNonQuery connection deps globalConf + |> SqlCommand.executeNonQuery connection deps conf outcome =! 0 PartialCallCounter.assertEqual callCounter 1 1 } @@ -37,11 +37,11 @@ let ``leave the connection open if initially open`` () = Mocks.NonQuery 1 |> Mocks.makeConnection "toto" ConnectionState.Connecting openCallback closeCallback let deps = Mocks.makeDeps None - let globalConf = Mocks.makeGlobalConf None + let conf = Mocks.makeConf None let! outcome = SqlCommand.text "update" |> SqlCommand.noLogger - |> SqlCommand.executeNonQuery connection deps globalConf + |> SqlCommand.executeNonQuery connection deps conf outcome =! 1 PartialCallCounter.assertEqual callCounter 0 0 } @@ -55,10 +55,10 @@ let ``log for all events on globalLogger if connection initially closed`` () = Mocks.NonQuery 2 |> Mocks.makeConnection "toto" ConnectionState.Closed openCallback closeCallback let deps = Mocks.makeDeps None - let globalConf = Mocks.makeGlobalConf (Some loggerCallback) + let conf = Mocks.makeConf (Some loggerCallback) let! outcome = SqlCommand.text "update" - |> SqlCommand.executeNonQuery connection deps globalConf + |> SqlCommand.executeNonQuery connection deps conf outcome =! 2 FullCallCounter.assertEqual callCounter 1 1 1 1 1 1 } @@ -72,10 +72,10 @@ let ``log just command events on globalLogger if connection initially not closed Mocks.NonQuery 3 |> Mocks.makeConnection "toto" ConnectionState.Connecting openCallback closeCallback let deps = Mocks.makeDeps None - let globalConf = Mocks.makeGlobalConf (Some loggerCallback) + let conf = Mocks.makeConf (Some loggerCallback) let! outcome = SqlCommand.text "update" - |> SqlCommand.executeNonQuery connection deps globalConf + |> SqlCommand.executeNonQuery connection deps conf outcome =! 3 FullCallCounter.assertEqual callCounter 0 0 0 0 1 1 } diff --git a/Vp.FSharp.Sql.Tests/SqlCommand for executeScalar should.fs b/Vp.FSharp.Sql.Tests/SqlCommand for executeScalar should.fs index 1e6a03e..2924008 100644 --- a/Vp.FSharp.Sql.Tests/SqlCommand for executeScalar should.fs +++ b/Vp.FSharp.Sql.Tests/SqlCommand for executeScalar should.fs @@ -29,11 +29,11 @@ let ``open and then close the connection if initially closed`` () = Mocks.Reader (Mocks.makeReader data) |> Mocks.makeConnection "toto" ConnectionState.Closed openCallback closeCallback let deps = Mocks.makeDeps None - let globalConf = Mocks.makeGlobalConf None + let conf = Mocks.makeConf None let! outcome = SqlCommand.text "select 1" |> SqlCommand.noLogger - |> SqlCommand.executeScalar connection deps globalConf + |> SqlCommand.executeScalar connection deps conf outcome =! 14 PartialCallCounter.assertEqual callCounter 1 1 } @@ -57,11 +57,11 @@ let ``leave the connection open if initially open`` () = Mocks.Reader (Mocks.makeReader data) |> Mocks.makeConnection "toto" ConnectionState.Connecting openCallback closeCallback let deps = Mocks.makeDeps None - let globalConf = Mocks.makeGlobalConf None + let conf = Mocks.makeConf None let! outcome = SqlCommand.text "select 1" |> SqlCommand.noLogger - |> SqlCommand.executeScalar connection deps globalConf + |> SqlCommand.executeScalar connection deps conf outcome =! 15 PartialCallCounter.assertEqual callCounter 0 0 } @@ -84,10 +84,10 @@ let ``log for all events on globalLogger if connection initially closed`` () = Mocks.Reader (Mocks.makeReader data) |> Mocks.makeConnection "toto" ConnectionState.Closed openCallback closeCallback let deps = Mocks.makeDeps None - let globalConf = Mocks.makeGlobalConf (Some loggerCallback) + let conf = Mocks.makeConf (Some loggerCallback) let! outcome = SqlCommand.text "select 1" - |> SqlCommand.executeScalar connection deps globalConf + |> SqlCommand.executeScalar connection deps conf outcome =! 16 FullCallCounter.assertEqual callCounter 1 1 1 1 1 1 } @@ -110,10 +110,10 @@ let ``log for just command events on globalLogger if connection initially not cl Mocks.Reader (Mocks.makeReader data) |> Mocks.makeConnection "toto" ConnectionState.Connecting openCallback closeCallback let deps = Mocks.makeDeps None - let globalConf = Mocks.makeGlobalConf (Some loggerCallback) + let conf = Mocks.makeConf (Some loggerCallback) let! outcome = SqlCommand.text "select 1" - |> SqlCommand.executeScalar connection deps globalConf + |> SqlCommand.executeScalar connection deps conf outcome =! 17 FullCallCounter.assertEqual callCounter 0 0 0 0 1 1 } diff --git a/Vp.FSharp.Sql.Tests/SqlCommand for executeScalarOrNone should.fs b/Vp.FSharp.Sql.Tests/SqlCommand for executeScalarOrNone should.fs index 92bff9f..b1c1b81 100644 --- a/Vp.FSharp.Sql.Tests/SqlCommand for executeScalarOrNone should.fs +++ b/Vp.FSharp.Sql.Tests/SqlCommand for executeScalarOrNone should.fs @@ -30,11 +30,11 @@ let ``open and then close the connection if initially closed`` () = Mocks.Reader (Mocks.makeReader data) |> Mocks.makeConnection "toto" ConnectionState.Closed openCallback closeCallback let deps = Mocks.makeDeps None - let globalConf = Mocks.makeGlobalConf None + let conf = Mocks.makeConf None let! outcome = SqlCommand.text "select 1" |> SqlCommand.noLogger - |> SqlCommand.executeScalarOrNone connection deps globalConf + |> SqlCommand.executeScalarOrNone connection deps conf outcome.IsSome =! true outcome |> Option.defaultValue 42 @@ -62,11 +62,11 @@ let ``leave the connection open if initially not closed`` () = Mocks.Reader (Mocks.makeReader data) |> Mocks.makeConnection "toto" ConnectionState.Connecting openCallback closeCallback let deps = Mocks.makeDeps None - let globalConf = Mocks.makeGlobalConf None + let conf = Mocks.makeConf None let! outcome = SqlCommand.text "select 1" |> SqlCommand.noLogger - |> SqlCommand.executeScalarOrNone connection deps globalConf + |> SqlCommand.executeScalarOrNone connection deps conf outcome.IsSome =! true outcome |> Option.defaultValue 42 @@ -93,10 +93,10 @@ let ``log for all events on globalLogger if connection initially closed`` () = Mocks.Reader (Mocks.makeReader data) |> Mocks.makeConnection "toto" ConnectionState.Closed openCallback closeCallback let deps = Mocks.makeDeps None - let globalConf = Mocks.makeGlobalConf (Some loggerCallback) + let conf = Mocks.makeConf (Some loggerCallback) let! outcome = SqlCommand.text "select 1" - |> SqlCommand.executeScalarOrNone connection deps globalConf + |> SqlCommand.executeScalarOrNone connection deps conf outcome.IsSome =! true outcome |> Option.defaultValue 42 @@ -122,10 +122,10 @@ let ``log for just command events on globalLogger if connection initially not cl Mocks.Reader (Mocks.makeReader data) |> Mocks.makeConnection "toto" ConnectionState.Connecting openCallback closeCallback let deps = Mocks.makeDeps None - let globalConf = Mocks.makeGlobalConf (Some loggerCallback) + let conf = Mocks.makeConf (Some loggerCallback) let! outcome = SqlCommand.text "select 1" - |> SqlCommand.executeScalarOrNone connection deps globalConf + |> SqlCommand.executeScalarOrNone connection deps conf outcome.IsSome =! true outcome |> Option.defaultValue 42 @@ -153,11 +153,11 @@ let ``open and then close connection if initially closed and retrieve None`` () Mocks.Reader (Mocks.makeReader data) |> Mocks.makeConnection "toto" ConnectionState.Closed openCallback closeCallback let deps = Mocks.makeDeps None - let globalConf = Mocks.makeGlobalConf None + let conf = Mocks.makeConf None let! outcome = SqlCommand.text "select 1" |> SqlCommand.noLogger - |> SqlCommand.executeScalarOrNone connection deps globalConf + |> SqlCommand.executeScalarOrNone connection deps conf outcome.IsNone =! true PartialCallCounter.assertEqual callCounter 1 1 } @@ -181,11 +181,11 @@ let ``leave connection open if initially not closed and retrieve None`` () = Mocks.Reader (Mocks.makeReader data) |> Mocks.makeConnection "toto" ConnectionState.Connecting openCallback closeCallback let deps = Mocks.makeDeps None - let globalConf = Mocks.makeGlobalConf None + let conf = Mocks.makeConf None let! outcome = SqlCommand.text "select 1" |> SqlCommand.noLogger - |> SqlCommand.executeScalarOrNone connection deps globalConf + |> SqlCommand.executeScalarOrNone connection deps conf outcome.IsNone =! true PartialCallCounter.assertEqual callCounter 0 0 } @@ -208,10 +208,10 @@ let ``log for all events on globalLogger if connection initially closed and retr Mocks.Reader (Mocks.makeReader data) |> Mocks.makeConnection "toto" ConnectionState.Closed openCallback closeCallback let deps = Mocks.makeDeps None - let globalConf = Mocks.makeGlobalConf (Some loggerCallback) + let conf = Mocks.makeConf (Some loggerCallback) let! outcome = SqlCommand.text "select 1" - |> SqlCommand.executeScalarOrNone connection deps globalConf + |> SqlCommand.executeScalarOrNone connection deps conf outcome.IsNone =! true FullCallCounter.assertEqual callCounter 1 1 1 1 1 1 } @@ -234,10 +234,10 @@ let ``log for just command events on globalLogger if connection initially not cl Mocks.Reader (Mocks.makeReader data) |> Mocks.makeConnection "toto" ConnectionState.Connecting openCallback closeCallback let deps = Mocks.makeDeps None - let globalConf = Mocks.makeGlobalConf (Some loggerCallback) + let conf = Mocks.makeConf (Some loggerCallback) let! outcome = SqlCommand.text "select 1" - |> SqlCommand.executeScalarOrNone connection deps globalConf + |> SqlCommand.executeScalarOrNone connection deps conf outcome.IsNone =! true FullCallCounter.assertEqual callCounter 0 0 0 0 1 1 } diff --git a/Vp.FSharp.Sql.Tests/SqlCommand for queryAsyncSeq should.fs b/Vp.FSharp.Sql.Tests/SqlCommand for queryAsyncSeq should.fs index 8f71163..baa5864 100644 --- a/Vp.FSharp.Sql.Tests/SqlCommand for queryAsyncSeq should.fs +++ b/Vp.FSharp.Sql.Tests/SqlCommand for queryAsyncSeq should.fs @@ -64,11 +64,11 @@ let ``open and then close the connection if initially closed and access value by Mocks.Reader (Mocks.makeReader data) |> Mocks.makeConnection "toto" ConnectionState.Closed openCallback closeCallback let deps = Mocks.makeDeps None - let globalConf = Mocks.makeGlobalConf None + let conf = Mocks.makeConf None let outcome = SqlCommand.text "select 1" |> SqlCommand.noLogger - |> SqlCommand.queryAsyncSeq connection deps globalConf + |> SqlCommand.queryAsyncSeq connection deps conf (readValueByFieldName ([2;1;0] |> toFieldNames)) |> AsyncSeq.toListSynchronously |> List.sortBy (fun values -> snd values.[0]) @@ -105,11 +105,11 @@ let ``open and then close the connection if initially closed and access value by Mocks.Reader (Mocks.makeReader data) |> Mocks.makeConnection "toto" ConnectionState.Closed openCallback closeCallback let deps = Mocks.makeDeps None - let globalConf = Mocks.makeGlobalConf None + let conf = Mocks.makeConf None let outcome = SqlCommand.text "select 1" |> SqlCommand.noLogger - |> SqlCommand.queryAsyncSeq connection deps globalConf + |> SqlCommand.queryAsyncSeq connection deps conf (readValueByIndex [2;1;0]) |> AsyncSeq.toListSynchronously |> List.sortBy (fun values -> values.[0]) @@ -146,11 +146,11 @@ let ``open and then close the connection if initially closed and access valueOrN Mocks.Reader (Mocks.makeReader data) |> Mocks.makeConnection "toto" ConnectionState.Closed openCallback closeCallback let deps = Mocks.makeDeps None - let globalConf = Mocks.makeGlobalConf None + let conf = Mocks.makeConf None let outcome = SqlCommand.text "select 1" |> SqlCommand.noLogger - |> SqlCommand.queryAsyncSeq connection deps globalConf + |> SqlCommand.queryAsyncSeq connection deps conf (readValueOrNoneByFieldName (toFieldNames [2;1;0])) |> AsyncSeq.toListSynchronously |> List.sortBy (fun values -> snd values.[0]) @@ -187,11 +187,11 @@ let ``open and then close connection if initially closed and access valueOrNone Mocks.Reader (Mocks.makeReader data) |> Mocks.makeConnection "toto" ConnectionState.Closed openCallback closeCallback let deps = Mocks.makeDeps None - let globalConf = Mocks.makeGlobalConf None + let conf = Mocks.makeConf None let outcome = SqlCommand.text "select 1" |> SqlCommand.noLogger - |> SqlCommand.queryAsyncSeq connection deps globalConf + |> SqlCommand.queryAsyncSeq connection deps conf (readValueOrNoneByIndex [2;1;0]) |> AsyncSeq.toListSynchronously |> List.sortBy (fun values -> values.[0]) @@ -228,11 +228,11 @@ let ``leave the connection open if initially not closed with access valueOrNone Mocks.Reader (Mocks.makeReader data) |> Mocks.makeConnection "toto" ConnectionState.Connecting openCallback closeCallback let deps = Mocks.makeDeps None - let globalConf = Mocks.makeGlobalConf None + let conf = Mocks.makeConf None let outcome = SqlCommand.text "select 1" |> SqlCommand.noLogger - |> SqlCommand.queryAsyncSeq connection deps globalConf + |> SqlCommand.queryAsyncSeq connection deps conf (readValueOrNoneByIndex [2;1;0]) |> AsyncSeq.toListSynchronously |> List.sortBy (fun values -> values.[0]) @@ -269,10 +269,10 @@ let ``log all events on globalLogger if connection initially closed with access Mocks.Reader (Mocks.makeReader data) |> Mocks.makeConnection "toto" ConnectionState.Closed openCallback closeCallback let deps = Mocks.makeDeps None - let globalConf = Mocks.makeGlobalConf (Some loggerCallback) + let conf = Mocks.makeConf (Some loggerCallback) let outcome = SqlCommand.text "select 1" - |> SqlCommand.queryAsyncSeq connection deps globalConf + |> SqlCommand.queryAsyncSeq connection deps conf (readValueOrNoneByIndex [2;1;0]) |> AsyncSeq.toListSynchronously |> List.sortBy (fun values -> values.[0]) @@ -309,10 +309,10 @@ let ``log for just command events on globalLogger if connection initially not cl Mocks.Reader (Mocks.makeReader data) |> Mocks.makeConnection "toto" ConnectionState.Connecting openCallback closeCallback let deps = Mocks.makeDeps None - let globalConf = Mocks.makeGlobalConf (Some loggerCallback) + let conf = Mocks.makeConf (Some loggerCallback) let outcome = SqlCommand.text "select 1" - |> SqlCommand.queryAsyncSeq connection deps globalConf + |> SqlCommand.queryAsyncSeq connection deps conf (readValueOrNoneByIndex [2;1;0]) |> AsyncSeq.toListSynchronously |> List.sortBy (fun values -> values.[0]) diff --git a/Vp.FSharp.Sql.Tests/SqlCommand for querySetListN should.fs b/Vp.FSharp.Sql.Tests/SqlCommand for querySetListN should.fs index 02cbcdd..c702d1f 100644 --- a/Vp.FSharp.Sql.Tests/SqlCommand for querySetListN should.fs +++ b/Vp.FSharp.Sql.Tests/SqlCommand for querySetListN should.fs @@ -41,12 +41,12 @@ let ``have querySetList should open and then close the connection if initially c Mocks.Reader (Mocks.makeReader data) |> Mocks.makeConnection "toto" ConnectionState.Closed openCallback closeCallback let deps = Mocks.makeDeps None - let globalConf = Mocks.makeGlobalConf None + let conf = Mocks.makeConf None let! r0 = SqlCommand.text "select 1" |> SqlCommand.noLogger |> SqlCommand.querySetList connection - deps globalConf + deps conf (readValueByIndex columnsIndex 0) r0.Length =! 2 r0 =! ([1..6] |> List.splitInto 2) @@ -68,11 +68,11 @@ let ``have querySetList2 should open and then close the connection if initially Mocks.Reader (Mocks.makeReader data) |> Mocks.makeConnection "toto" ConnectionState.Closed openCallback closeCallback let deps = Mocks.makeDeps None - let globalConf = Mocks.makeGlobalConf None + let conf = Mocks.makeConf None let! (r0, r1) = SqlCommand.text "select 1" |> SqlCommand.noLogger - |> SqlCommand.querySetList2 connection deps globalConf + |> SqlCommand.querySetList2 connection deps conf (readValueByIndex columnsIndex 0) (readValueByIndex columnsIndex 1) r0.Length =! 2 @@ -99,11 +99,11 @@ let ``have querySetList3 open and then close the connection if initially closed Mocks.Reader (Mocks.makeReader data) |> Mocks.makeConnection "toto" ConnectionState.Closed openCallback closeCallback let deps = Mocks.makeDeps None - let globalConf = Mocks.makeGlobalConf None + let conf = Mocks.makeConf None let! (r0, r1, r2) = SqlCommand.text "select 1" |> SqlCommand.noLogger - |> SqlCommand.querySetList3 connection deps globalConf + |> SqlCommand.querySetList3 connection deps conf (readValueByIndex columnsIndex 0) (readValueByIndex columnsIndex 1) (readValueByIndex columnsIndex 2) diff --git a/Vp.FSharp.Sql/SqlCommand.fs b/Vp.FSharp.Sql/SqlCommand.fs index 0e858e7..f7a0c57 100644 --- a/Vp.FSharp.Sql/SqlCommand.fs +++ b/Vp.FSharp.Sql/SqlCommand.fs @@ -29,7 +29,7 @@ module Vp.FSharp.Sql.SqlCommand CommandType = DefaultCommandType Prepare = DefaultPrepare Transaction = None - Logger = Global } + Logger = Conf } /// Initialize a command definition with the given text contained in the given string. let text value = { defaultCommandDefinition() with Text = Text.Single value } @@ -97,9 +97,9 @@ module Vp.FSharp.Sql.SqlCommand connection.OpenAsync(cancellationToken) |> Async.AwaitTask - let private log4 globalConf commandDefinition sqlLog = + let private log4 conf commandDefinition sqlLog = match commandDefinition.Logger with - | Global -> globalConf.DefaultLogger + | Conf -> conf.DefaultLogger | Override logging -> Some logging | Nothing -> None |> Option.iter (fun f -> f sqlLog) @@ -127,11 +127,11 @@ module Vp.FSharp.Sql.SqlCommand } /// Return the sets of rows as an AsyncSeq accordingly to the command definition. - let queryAsyncSeq (connection: #DbConnection) deps globalConf read commandDefinition = + let queryAsyncSeq (connection: #DbConnection) deps conf read commandDefinition = asyncSeq { let! linkedToken = Async.linkedTokenSourceFrom commandDefinition.CancellationToken let wasClosed = connection.State = ConnectionState.Closed - let log sqlLog = log4 globalConf commandDefinition sqlLog + let log sqlLog = log4 conf commandDefinition sqlLog let connectionStopwatch = Stopwatch() let commandStopwatch = Stopwatch() use! command = setupCommand deps commandDefinition linkedToken connection @@ -168,23 +168,23 @@ module Vp.FSharp.Sql.SqlCommand } /// Return the sets of rows as a list accordingly to the command definition. - let queryList connection deps globalConf read commandDefinition = - queryAsyncSeq connection deps globalConf read commandDefinition + let queryList connection deps conf read commandDefinition = + queryAsyncSeq connection deps conf read commandDefinition |> AsyncSeq.toListAsync /// Return the first set of rows as a list accordingly to the command definition. - let querySetList (connection: #DbConnection) deps globalConf read commandDefinition = + let querySetList (connection: #DbConnection) deps conf read commandDefinition = async { let setList = ResizeArray() let readRecord setIndex recordIndex recordReader = if setIndex = 0 then setList.Add(read recordIndex recordReader) else () - do! queryAsyncSeq connection deps globalConf readRecord commandDefinition |> AsyncSeq.consume + do! queryAsyncSeq connection deps conf readRecord commandDefinition |> AsyncSeq.consume return setList |> Seq.toList } /// Return the 2 first sets of rows as a tuple of 2 lists accordingly to the command definition. - let querySetList2 connection deps globalConf read1 read2 commandDefinition = + let querySetList2 connection deps conf read1 read2 commandDefinition = async { let set1List = ResizeArray() let set2List = ResizeArray() @@ -192,12 +192,12 @@ module Vp.FSharp.Sql.SqlCommand if setIndex = 0 then set1List.Add(read1 recordIndex recordReader) elif setIndex = 1 then set2List.Add(read2 recordIndex recordReader) else () - do! queryAsyncSeq connection deps globalConf readRecord commandDefinition |> AsyncSeq.consume + do! queryAsyncSeq connection deps conf readRecord commandDefinition |> AsyncSeq.consume return (set1List |> Seq.toList, set2List |> Seq.toList) } /// Return the 3 first sets of rows as a tuple of 3 lists accordingly to the command definition. - let querySetList3 connection deps globalConf read1 read2 read3 commandDefinition = + let querySetList3 connection deps conf read1 read2 read3 commandDefinition = async { let set1List = ResizeArray() let set2List = ResizeArray() @@ -207,18 +207,18 @@ module Vp.FSharp.Sql.SqlCommand elif setIndex = 1 then set2List.Add(read2 recordIndex recordReader) elif setIndex = 2 then set3List.Add(read3 recordIndex recordReader) else () - do! queryAsyncSeq connection deps globalConf readRecord commandDefinition |> AsyncSeq.consume + do! queryAsyncSeq connection deps conf readRecord commandDefinition |> AsyncSeq.consume return (set1List |> Seq.toList, set2List |> Seq.toList, set3List |> Seq.toList) } /// Execute the command accordingly to its definition and, /// - return the first cell value, if it is available and of the given type. /// - throw an exception, otherwise. - let executeScalar<'Scalar, .. > (connection: #DbConnection) deps globalConf commandDefinition = + let executeScalar<'Scalar, .. > (connection: #DbConnection) deps conf commandDefinition = async { let! linkedToken = Async.linkedTokenSourceFrom commandDefinition.CancellationToken let wasClosed = connection.State = ConnectionState.Closed - let log sqlLog = log4 globalConf commandDefinition sqlLog + let log sqlLog = log4 conf commandDefinition sqlLog let connectionStopwatch = Stopwatch() let commandStopwatch = Stopwatch() use! command = setupCommand deps commandDefinition linkedToken connection @@ -254,12 +254,12 @@ module Vp.FSharp.Sql.SqlCommand /// - return Some, if the first cell is available and of the given type. /// - return None, if first cell is DbNull. /// - throw an exception, otherwise. - let executeScalarOrNone<'Scalar, .. > (connection: #DbConnection) deps globalConf commandDefinition = + let executeScalarOrNone<'Scalar, .. > (connection: #DbConnection) deps conf commandDefinition = async { let! linkedToken = Async.linkedTokenSourceFrom commandDefinition.CancellationToken let wasClosed = connection.State = ConnectionState.Closed - let log sqlLog = log4 globalConf commandDefinition sqlLog + let log sqlLog = log4 conf commandDefinition sqlLog let connectionStopwatch = Stopwatch() let commandStopwatch = Stopwatch() use! command = setupCommand deps commandDefinition linkedToken connection @@ -289,11 +289,11 @@ module Vp.FSharp.Sql.SqlCommand } /// Execute the command accordingly to its definition and, return the number of rows affected. - let executeNonQuery (connection: #DbConnection) deps globalConf commandDefinition = + let executeNonQuery (connection: #DbConnection) deps conf commandDefinition = async { let! linkedToken = Async.linkedTokenSourceFrom commandDefinition.CancellationToken let wasClosed = connection.State = ConnectionState.Closed - let log sqlLog = log4 globalConf commandDefinition sqlLog + let log sqlLog = log4 conf commandDefinition sqlLog let connectionStopwatch = Stopwatch() let commandStopwatch = Stopwatch() use! command = setupCommand deps commandDefinition linkedToken connection diff --git a/Vp.FSharp.Sql/Types.fs b/Vp.FSharp.Sql/Types.fs index 9a2eca3..7bf6ee7 100644 --- a/Vp.FSharp.Sql/Types.fs +++ b/Vp.FSharp.Sql/Types.fs @@ -24,7 +24,7 @@ type SqlLog<'DbConnection, 'DbCommand type LoggerKind<'DbConnection, 'DbCommand when 'DbConnection :> DbConnection and 'DbCommand :> DbCommand> = - | Global + | Conf | Override of (SqlLog<'DbConnection, 'DbCommand> -> unit) | Nothing @@ -43,11 +43,30 @@ type CommandDefinition<'DbConnection, 'DbTransaction, 'DbCommand, 'DbParameter, Transaction: 'DbTransaction option Logger: LoggerKind<'DbConnection, 'DbCommand> } -type SqlGlobalConf<'DbConnection, 'DbCommand - when 'DbConnection :> DbConnection - and 'DbCommand :> DbCommand> = +type SqlConf<'DbConnection, 'DbCommand + when 'DbConnection :> DbConnection + and 'DbCommand :> DbCommand> = { DefaultLogger: (SqlLog<'DbConnection, 'DbCommand> -> unit) option } +[] +module SqlConf = + let internal defaultValue() = { DefaultLogger = None } + + let logger value conf = { conf with DefaultLogger = Some value } + let noLogger conf = { conf with DefaultLogger = None } + +[] +type SqlGlobalConf<'DbConnection, 'DbCommand + when 'DbConnection :> DbConnection + and 'DbCommand :> DbCommand> = + + static member private instance: SqlConf<'DbConnection, 'DbCommand> = + SqlConf.defaultValue() + + static member logger(value) = SqlConf.logger value SqlGlobalConf<'DbConnection, 'DbCommand>.instance + static member noLogger() = SqlConf.noLogger SqlGlobalConf<'DbConnection, 'DbCommand>.instance + static member Logger with get () = SqlGlobalConf<'DbConnection, 'DbCommand>.instance.DefaultLogger + type SqlDeps<'DbConnection, 'DbTransaction, 'DbCommand, 'DbParameter, 'DbDataReader, 'DbType when 'DbConnection :> DbConnection and 'DbTransaction :> DbTransaction