FAQ
1. How do I specify default value for a parameter?
SqlCommandProvider
generates parameters for all unbound T-SQL script variables.
By definition those variables don't have default value.
One workaround is to assume NULL as default value (see next item).
The other option is to have default value logic in application code.
If you feel strongly about having default parameter values defined in T-SQL
consider wrapping it into stored procedure or function. Both accept default values for parameters.
SqlProgrammabilityProvider
supports this scenario by mapping it to .NET method parameters with default values.
2. How do I pass NULL as parameter value?
By default SqlCommandProvider treat all parameters as mandatory.
1: 2: 3: |
let echoOnly = new SqlCommandProvider<"SELECT ISNULL(@x, 42)", connectionString, SingleRow = true>(connectionString) echoOnly.Execute( x = (* must pass int value here *) 1) |
There is no way to pass NULL as value for parameter @x
although the query knows how to handle it.
To resolve the issue specify AllParametersOptional = true
as static parameter to type provider.
It makes all parameters of type option<_>
with default value None
.
Note This is not same as F# optional parameters but mimics them closely. Unfortunately type providers in general restricted to emit .NET 1.1 types only. No generics, no F# specific types/annotations which includes optional parameters.
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: |
let echoOr42 = new SqlCommandProvider<" SELECT ISNULL(@x, 42) ", connectionString, SingleRow = true, AllParametersOptional = true>(connectionString) // Pass parameter value. Specifying Some constructor is mandatrory. echoOr42.Execute( Some 1) // Pass NULL by omitting parameter echoOr42.Execute() //Pass NULL explicitly echoOr42.Execute( x = None) |
3. How do I do dynamic filter?
Dynamic filters is when your T-SQL command accepts multiple input arguments and is supposed to determine which columns to filter, if at all, based on that input.
Following command allows to filter dynamically information on sales people based on country and minimal sales amount.
4. Why do I get compile time error " ... The undeclared parameter '@p1' is used more than once in the batch being analyzed."?
This is a limitation of sys.sp_describe_undeclared_parameters.
To work around this limitation, you can declare another variable in your script:
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: |
// this one would fail with above mentionned error: //let echoFail = // new SqlCommandProvider<" // select 'hello' + @name, 'your name is :' @name // ", connectionString, SingleRow = true>(connectionString) // this one is ok as @name parameter is used only once in the statement: let echoOk = new SqlCommandProvider< @" declare @theName nvarchar(max) set @theName = @name select 'hello' + @theName, 'your name is :' + @theName ", connectionString, ResultType.Tuples, SingleRow = true>(connectionString) |
5. Exposing types generated by SqlCommandProvider
6. Reusing typed record with a custom command
With a datareader obtained from a custom command you can still reuse the typed record definition through the DynamicRecord class.
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: |
[<Literal>] let getDatesQuery = "SELECT GETDATE() AS Now, GETUTCDATE() AS UtcNow" type GetDates = SqlCommandProvider<getDatesQuery, connectionString> open System.Data.SqlClient type SqlDataReader with member this.ToRecords<'T>() = seq { while this.Read() do let data = dict [ for i = 0 to this.VisibleFieldCount - 1 do yield this.GetName(i), this.GetValue(i) ] yield FSharp.Data.SqlClient.DynamicRecord(data) |> box |> unbox<'T> } let xs = use conn = new SqlConnection(connectionString) conn.Open() let cmd = new System.Data.SqlClient.SqlCommand(getDatesQuery, conn) cmd.ExecuteReader().ToRecords<GetDates.Record>() |> Seq.toArray |
7. Why do I sometimes get ExecuteSingle and AsyncExecuteSingle options with the SqlProgrammabilityProvider?
There are two cases when you would get those extra methods.
- Referencing a stored procedure that returns some sort of result set you will get these extra methods.
- Referencing a Table Valued Function
In both cases the methods return Option<'T>
(or Async<Option<'T>>
) rather than Seq<'T>
(or Async<Seq<'T>>
)
Full name: Faq.echoOnly
Full name: Faq.connectionString
Full name: Faq.echoOr42
Full name: Faq.echoOk
| Records = 0
| Tuples = 1
| DataTable = 2
| DataReader = 3
Full name: FSharp.Data.ResultType
type LiteralAttribute =
inherit Attribute
new : unit -> LiteralAttribute
Full name: Microsoft.FSharp.Core.LiteralAttribute
--------------------
new : unit -> LiteralAttribute
Full name: Faq.getDatesQuery
Full name: Faq.GetDates
inherit DbDataReader
member Close : unit -> unit
member Depth : int
member FieldCount : int
member GetBoolean : i:int -> bool
member GetByte : i:int -> byte
member GetBytes : i:int * dataIndex:int64 * buffer:byte[] * bufferIndex:int * length:int -> int64
member GetChar : i:int -> char
member GetChars : i:int * dataIndex:int64 * buffer:char[] * bufferIndex:int * length:int -> int64
member GetDataTypeName : i:int -> string
member GetDateTime : i:int -> DateTime
...
Full name: System.Data.SqlClient.SqlDataReader
Full name: Faq.ToRecords
val seq : sequence:seq<'T> -> seq<'T>
Full name: Microsoft.FSharp.Core.Operators.seq
--------------------
type seq<'T> = System.Collections.Generic.IEnumerable<'T>
Full name: Microsoft.FSharp.Collections.seq<_>
Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.dict
type DynamicRecord =
inherit DynamicObject
new : data:IDictionary<string,obj> -> DynamicRecord
override Equals : other:obj -> bool
override GetDynamicMemberNames : unit -> IEnumerable<string>
override GetHashCode : unit -> int
override ToString : unit -> string
override TryGetMember : binder:GetMemberBinder * result:byref<obj> -> bool
override TrySetMember : binder:SetMemberBinder * result:obj -> bool
member private Data : IDictionary<string,obj>
member Item : key:string -> obj with get
Full name: FSharp.Data.SqlClient.DynamicRecord
--------------------
new : data:System.Collections.Generic.IDictionary<string,obj> -> SqlClient.DynamicRecord
Full name: Microsoft.FSharp.Core.Operators.box
Full name: Microsoft.FSharp.Core.Operators.unbox
Full name: Faq.xs
type SqlConnection =
inherit DbConnection
new : unit -> SqlConnection + 1 overload
member BeginTransaction : unit -> SqlTransaction + 3 overloads
member ChangeDatabase : database:string -> unit
member Close : unit -> unit
member ConnectionString : string with get, set
member ConnectionTimeout : int
member CreateCommand : unit -> SqlCommand
member DataSource : string
member Database : string
member EnlistDistributedTransaction : transaction:ITransaction -> unit
...
Full name: System.Data.SqlClient.SqlConnection
--------------------
SqlConnection() : unit
SqlConnection(connectionString: string) : unit
type SqlCommand =
inherit DbCommand
new : unit -> SqlCommand + 3 overloads
member BeginExecuteNonQuery : unit -> IAsyncResult + 1 overload
member BeginExecuteReader : unit -> IAsyncResult + 3 overloads
member BeginExecuteXmlReader : unit -> IAsyncResult + 1 overload
member Cancel : unit -> unit
member Clone : unit -> SqlCommand
member CommandText : string with get, set
member CommandTimeout : int with get, set
member CommandType : CommandType with get, set
member Connection : SqlConnection with get, set
...
Full name: System.Data.SqlClient.SqlCommand
--------------------
SqlCommand() : unit
SqlCommand(cmdText: string) : unit
SqlCommand(cmdText: string, connection: SqlConnection) : unit
SqlCommand(cmdText: string, connection: SqlConnection, transaction: SqlTransaction) : unit
SqlCommand.ExecuteReader(behavior: System.Data.CommandBehavior) : SqlDataReader
from Microsoft.FSharp.Collections
Full name: Microsoft.FSharp.Collections.Seq.toArray