在F# best way to set up a SQLCommand with parameters
中给出了一些非常简洁的解决方案来构造SQLCommand输入参数。现在我需要做一些输出参数来调用一个返回两个输出参数的存储过程。
到目前为止,我有:
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()
哪个有效,但我发现它很丑陋而且太迫切了。如何扩展上一个示例中的解决方案(尤其是我现在正在使用的Tomas)来处理输出参数?因此输入和输出将在同一命令中发布。
所以我尝试了这个:
type Command =
{ Query : string
Timeout : int
Parameters : (string * Parameter) list
OutParameters : Option<(string * OutParameter)> list}
接着是:
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
match cmd.OutParameters with
| Some <string * OutParameter> list ->
for name, par in list 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
| _ -> ()
但我无法在接近结束时计算出匹配的语法。我试过了:
一些列表 - &gt;并得到了
错误52此表达式应具有类型 选项列表但这里有类型 &#39;选项
然后我尝试了:
| Some Option<string * OutParameter> list ->
得到了同样的错误,所以我试过了:
| Some <string * OutParameter> list ->
得到了一个不同的错误:
错误53模式中出现意外标识符。预期的中缀运营商, 引号或其他标记。
然后尝试了:
| Some <(string * OutParameter)> list ->
得到了错误:
错误53意外的符号&#39;(&#39;在模式中。预期的中缀运算符, 引号或其他标记。
最后尝试过:
| Some (string * OutParameter) list ->
再次出现第一个错误。
然后,我放弃了。
这里需要什么语法?
想出一个新的:
| Some list : (string * OutParameter) ->
for name, par in list do
但&#34;对于&#34;
的错误错误53意外的关键字&#39; for&#39;在类型
新尝试:
我想也许我可以定义一个函数来构建一个期望输出参数的sql命令,并且仍然使用第一个createSqlCommand函数。我试过这个:
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记录的成员创建Command记录吗?如果是这样,我该怎么做铸造? (它似乎既不是一个向上倾斜的人)
答案 0 :(得分:1)
托马斯当然有更好的资格来回答这个问题,但我会尝试一下。如果他确实回答了,那么看看我是否走在正确的轨道上会很有趣。我想我稍微离开了。
如果这并不顺利,请耐心等待,因为我不会测试它。我将基于the code Tomas gave us。
我认为我们需要一个新的OutParameter类型。
type OutParameter =
| OutInt
| OutVarChar of int // the length is needed?
在Command类型中,我们添加了一个名为OutParameters的额外字段。
type Command =
{ Query : string
Timeout : int
Parameters : (string * Parameter) list
OutParameters : (string * OutParameter) list }
在cmd函数中,必须添加它。
OutParameters =
[ "@errorCode", OutInt
"@errorMessage", OutVarChar 2000 ]
createSqlCommand函数现在还必须处理OutParameters。最后一个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
运行ExecuteNonQuery
后,您可以再次利用OutParameters列表来解析输出。
现在是一个提取值的函数。
let extractOutParameters (cmd: SqlCommand) (outParms: (string * OutParameter) list) =
outParms
|> List.map (fun (name, outType) ->
match outType with
| OutInt -> cmd.Parameters.Item(name).Value :?> int |> Int
| OutVarChar _ -> cmd.Parameters.Item(name).Value.ToString() |> VarChar
)
我完全不确定像这样投射这些值是好的,你可能应该匹配类型,以正确处理错误。测试一下。但这个小问题与我试图展示的内容没什么关系。
请注意,此函数使用Parameter类型返回值,而不是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"
即使返回了10个字段,您也可以将其记录在一个记录中,因此忽略您不想要的字段很简单。
如果你构建了另一个带有结果的功能,那么在调用它时你根本就不感兴趣,你可以这样称呼它。
startEngineAndGetStatus con "mainEngine" |> ignore