F# SQLF命令输出参数

F# SQLF命令输出参数,f#,sqlcommand,F#,Sqlcommand,在 对于构造SQLCommand输入参数,给出了一些非常简洁的解决方案。现在我需要做一些输出参数来调用一个返回两个输出参数的存储过程 type Command = { Query : string Timeout : int Parameters : (string * Parameter) list OutParameters : (string * OutParameter) list } 到目前为止,我已经: let cmd = (creat

对于构造SQLCommand输入参数,给出了一些非常简洁的解决方案。现在我需要做一些输出参数来调用一个返回两个输出参数的存储过程

type Command =
  { Query : string 
    Timeout : int
    Parameters : (string * Parameter) list
    OutParameters : (string * OutParameter) list }
到目前为止,我已经:

        let cmd = (createSqlCommand query conn)

        let pec = (new SqlParameter("@errorCode", SqlDbType.Int))
        pec.Direction <- ParameterDirection.Output
        ignore (cmd.Parameters.Add(pec))

        let pet = new SqlParameter("@errorMessage", SqlDbType.VarChar, 2000)
        pet.Direction <- ParameterDirection.Output
        ignore (cmd.Parameters.Add(pet))

        let rc = cmd.ExecuteNonQuery()
        let errorCode = cmd.Parameters.Item("@errorCode").Value.ToString()
        let errorText = cmd.Parameters.Item("@errorMessage").Value.ToString()
又犯了第一个错误

然后,我放弃了

这里需要什么语法

想出一个新的:

| Some list : (string * OutParameter) ->
        for name, par in list do
但这是错误的

错误53类型中出现意外的关键字“for”

新尝试:

我想也许我可以定义一个函数来构建一个需要输出参数的sql命令,并且仍然使用第一个createSqlCommand函数。我试过这个:

type Command = 
  { Query : string 
    Timeout : int
    Parameters : (string * Parameter) list 
    OutParameters : Option<(string * OutParameter)> list}
type OutCommand = 
  { Query : string 
    Timeout : int
    Parameters : (string * Parameter) list 
    OutParameters : (string * OutParameter) list 
  }

let createSqlCommandOut (cmd : OutCommand) connection = 
  let sql = createSqlCommand {cmd.Query; cmd.Timeout; cmd.Parameters} connection
  for name, par in cmd.OutParameters do
    let sqlParameter = 
        match par with
        | OutInt -> new SqlParameter(name, SqlDbType.Int)
        | OutVarChar len -> new SqlParameter(name, SqlDbType.VarChar, len)
    sqlParameter.Direction <- ParameterDirection.Output
    sql.Parameters.Add sqlParameter |> ignore
  sql 
其思想是获取传入的参数,并将其发送到原始函数来完成工作。你可能猜到了,这是行不通的。我得到了错误

错误53无效的对象、序列或记录表达式


在新函数中调用createSqlCommand时。这可能吗?我可以使用OutCommand记录的成员创建命令记录吗?如果是的话,我该怎么做?这似乎既不是一个向上向下的问题,托马斯当然更有资格回答这个问题,但我会尝试一下。如果他真的回答了,看看我是否在正确的轨道上会很有趣。我想我有点不对劲

如果运行不太好,请容忍我,因为我不会测试它。我将以此为基础

我想我们需要一种新的输出参数类型

type OutParameter =
  | OutInt
  | OutVarChar of int // the length is needed?
在命令类型中,我们添加了一个名为OutParameters的额外字段

type Command =
  { Query : string 
    Timeout : int
    Parameters : (string * Parameter) list
    OutParameters : (string * OutParameter) list }
在cmd函数中,必须添加此项

OutParameters = 
  [ "@errorCode", OutInt
    "@errorMessage", OutVarChar 2000 ]
函数createSqlCommand现在还必须处理输出参数。最后一个for循环是这里唯一的修改

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
  for name, par in cmd.OutParameters do
    let sqlParameter =
      match par with
      | OutInt -> new SqlParameter(name, SqlDbType.Int)
      | OutVarChar len -> new SqlParameter(name, SqlDbType.VarChar, len)
    sqlParameter.Direction <- ParameterDirection.Output
    sql.Parameters.Add sqlParameter |> ignore
  sql
我一点也不确定这样强制转换值是否好,您可能应该在类型上进行匹配,以正确处理错误。测试一下。但这是一个小问题,与我要演示的内容没有太大关系

请注意,此函数使用参数类型返回值,而不是OutParameter类型。在这一点上,我会考虑更改一个或两个类型的名称,以更好地反映它们的用法。

更新

您可以使用它为命令和查询创建特定函数。这是一个稍微有点假科迪什F片段

type UserInfo = { UserName: string; Name: string; LastLogin: DateTime }

let getUserInfo con userName : UserInfo =
    let cmd = {
        Query = "some sql to get the data"
        Timeout = 1000
        Parameters = ... the user name here
        OutParameters = ... the userName, Name and LastLogin here
        }
    let sqlCommand = createSqlCommand cmd con
    ... run the ExecuteNonQuery or whatever here
    let outs = extractOutParameters sqlCommand cmd.OutParameters
    {
        UserName = getValOfParam outs "@userName"
        Name = getValOfParam outs "@name"
        LastLogin = getValOfParam outs "@lastLogin"
    }
您必须创建函数getValOfParam,它只搜索具有正确名称的参数,并返回其值

然后可以像这样使用getUserInfo

let userInfo = getUserInfo con "john_smith"
startEngineAndGetStatus con "mainEngine" |> ignore
即使返回了10个字段,您也可以在一条记录中获得它们,因此忽略不需要的字段很简单

如果您构建了另一个函数,在调用它时,您对它的结果一点也不感兴趣,那么您会这样调用它

let userInfo = getUserInfo con "john_smith"
startEngineAndGetStatus con "mainEngine" |> ignore

我有点困惑,为什么必须提供2000年的长度,但我只是想当然地认为它是必要的。如果不是,那么显然可以进行简化。无论如何,在其他情况下,也可能需要传递有关如何解析输出数据的信息,这就说明了这一点。我认为必须为客户端分配存储以接收结果提供长度。否则它不知道查询返回什么。当然,我可以想象使用sp_descripe_first_result_set运行proc来查找实际长度。不过,这项计划太过分了。我在这里迷路了,使用参数类型返回值,而不是OutParameter。输出参数类型是否不工作?此外,在查询结束时,我需要将输出包含在两个变量中,而不是列表中。那容易打开吗?好的。我得到参数vs OutParameters注释。我花了几分钟另外一件事是,我能让输出参数可选吗?这看起来怎么样?创建一个函数,该函数接受输出参数并传递一个结果记录,其中包含字段ErrorCode和ErrorMessage。至于选项pun-designed,您可以以各种方式使用选项类型/关键字。更好的是,看一看,特别是结果类型。
type UserInfo = { UserName: string; Name: string; LastLogin: DateTime }

let getUserInfo con userName : UserInfo =
    let cmd = {
        Query = "some sql to get the data"
        Timeout = 1000
        Parameters = ... the user name here
        OutParameters = ... the userName, Name and LastLogin here
        }
    let sqlCommand = createSqlCommand cmd con
    ... run the ExecuteNonQuery or whatever here
    let outs = extractOutParameters sqlCommand cmd.OutParameters
    {
        UserName = getValOfParam outs "@userName"
        Name = getValOfParam outs "@name"
        LastLogin = getValOfParam outs "@lastLogin"
    }
let userInfo = getUserInfo con "john_smith"
startEngineAndGetStatus con "mainEngine" |> ignore