F# best way to set up a SQLCommand with parameters

扶醉桌前 提交于 2019-12-12 19:13:11

问题


My F# program needs to talk to SQL Server. In one part I have something like this:

 let workFlowDetailRuncommand = new SqlCommand(query, econnection) 
    workFlowDetailRuncommand.CommandTimeout <- 100000
    workFlowDetailRuncommand.Parameters.Add("@1", SqlDbType.Int).Value <- 42
    workFlowDetailRuncommand.Parameters.Add("@2", SqlDbType.VarChar).Value <- "answer"
    workFlowDetailRuncommand.Parameters.Add("@3", SqlDbType.VarChar).Value <- mydate.ToString("yyyy.MM.dd")
    workFlowDetailRuncommand.Parameters.Add("@4", SqlDbType.VarChar).Value <- "D. Adams"
    workFlowDetailRuncommand.Parameters.Add("@5", SqlDbType.DateTime).Value <- DateTime.Now
    workFlowDetailRuncommand.Parameters.Add("@6", SqlDbType.Text).Value <- filename

Is there a more idomatic way to do this (with less typing!) without setting the parameters one at a time like this.


回答1:


I think the answer from Bent gives you very nice DSL for constructing the standard SqlCommand objects. That might very well be just what you need - if you just want nicer syntax for creating a couple of commands, it will work perfectly.

If you wanted to do more things with your SQL commands, then the DSL has one limitation, which is that it is still based on the underlying mutable SqlCommand type - it looks functional, but it mutates objects under the cover, which might get you in trouble.

A more comprehensive option would be to define your own functional types to capture the domain - that is, the kind of queries you want to run:

type Parameter =
  | Int of int
  | VarChar of string
  | Text of string
  | DateTime of System.DateTime

type Command = 
  { Query : string 
    Timeout : int
    Parameters : (string * Parameter) list }

Then you can construct queries using normal F# types (and you could even implement a DSL like the one Bent suggested on top of this, while still keeping things immutable):

let cmd = 
  { Query = query
    Timeout = 100000 
    Parameters = 
      [ "@1", Int 42
        "@2", VarChar "answer"
        "@3", VarChar (mydate.ToString("yyyy.MM.dd"))
        "@4", VarChar "D. Adams"
        "@5", DateTime DateTime.Now
        "@6", Text filename ] }

The last bit would be to write a function that takes command and a connection and turns it into SqlCommand:

let createSqlCommand cmd connection = 
  let sql = new SqlCommand(cmd.Query, connection) 
  sql.CommandTimeout <- cmd.Timeout
  for name, par in cmd.Parameters do
    let sqlTyp, value = 
      match par with
      | Int n -> SqlDbType.Int, box n
      | VarChar s -> SqlDbType.VarChar, box s
      | Text s -> SqlDbType.Text, box s
      | DateTime dt -> SqlDbType.DateTime, box dt
    sql.Parameters.Add(name, sqlTyp).Value <- value
  sql 

What is the best approach will depend on your use case - and interacting with databases is inherently impure, so perhaps keeping things isolated and impure is perfectly fine. Though I wanted to show this as a possible option if you wanted to be more functional and focus on the domain (using the strong domain-driven modelling side of F#!).




回答2:


I haven't tested this.

Create some helper functions.

let createSqlCommand query connection =
    new SqlCommand(query, connection)

let setTimeout timeout (sqlCommand: SqlCommand) =
    sqlCommand.CommandTimeout <- timeout
    sqlCommand

let addInt name (value: int) (sqlCommand: SqlCommand) =
    sqlCommand.Parameters.Add(name, SqlDbType.Int).Value <- value
    sqlCommand

Use them like this.

let mySqlCommand =
    createSqlCommand someQuery someConnection
    |> setTimeout 100000
    |> addInt "@1" 41
    |> addString "@s" "Hello"
    |> addDateTime "@dt1" DateTime.Now
    |> addFloat "@f1" 5.1

If you have an int, it makes sense to always use SqlDbType.Int.

But if you have a string, there are several obvious candidates for field types. For that reason it may be a good idea to let the names of addXxx functions reflect the field types rather than the F#/.NET types. So that you would create addVarChar, addNVarChar, addChar, etc.

|> addInt "@i1" myInt
|> addDateTime "@dt1" myDateTime
|> addText "@tagText" myTagText
|> addNVarChar "@letter" myLetterBody


来源:https://stackoverflow.com/questions/42819003/f-best-way-to-set-up-a-sqlcommand-with-parameters

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!