gpt4 book ai didi

f# - 数据库连接和 F#

转载 作者:行者123 更新时间:2023-12-04 18:11:38 25 4
gpt4 key购买 nike

我编写了以下代码来在 F# 中执行 SQLServer StoredProc

module SqlUtility =
open System
open System.Data
open System.Data.SqlClient

SqlUtility.GetSqlConnection "MyDB"
|> Option.bind (fun con -> SqlUtility.GetSqlCommand "dbo.usp_MyStordProc" con)
|> Option.bind (fun cmd ->
let param1 = new SqlParameter("@User", SqlDbType.NVarChar, 50)
param1.Value <- user
cmd.Parameters.Add(param1) |> ignore
let param2 = new SqlParameter("@PolicyName", SqlDbType.NVarChar, 10)
param2.Value <- policyName
cmd.Parameters.Add(param2) |> ignore
Some(cmd)
)
|> Option.bind (fun cmd -> SqlUtility.ExecuteReader cmd)
|> Option.bind (fun rdr -> ExtractValue rdr)

let GetSqlConnection (conName : string) =
let conStr = ConfigHandler.GetConnectionString conName
try
let con = new SqlConnection(conStr)
con.Open()
Some(con)
with
| :? System.Exception as ex -> printfn "Failed to connect to DB %s with Error %s " conName ex.Message; None
| _ -> printfn "Failed to connect to DB %s" conName; None

let GetSqlCommand (spName : string) (con : SqlConnection) =
let cmd = new SqlCommand()
cmd.Connection <- con
cmd.CommandText <- spName
cmd.CommandType <- CommandType.StoredProcedure
Some(cmd)

let AddParameters (cmd : SqlCommand) (paramList : SqlParameter list) =
paramList |> List.iter (fun p -> cmd.Parameters.Add p |> ignore)

let ExecuteReader (cmd : SqlCommand ) =
try
Some(cmd.ExecuteReader())
with
| :? System.Exception as ex -> printfn "Failed to execute reader with error %s" ex.Message; None

我有这个代码的多个问题
  • 首先也是最重要的,Option.bind 的重复使用是非常令人恼火的......并且会增加噪音。我需要一种更清晰的方法来检查输出是否为 None,如果不是,则继续。
  • 最后应该有一个清理函数,我应该能够在其中关闭 + 处理读取器、命令和连接。但目前在管道的尽头,我只有读者。
  • 添加参数的函数......看起来它正在修改命令参数的“状态”,因为返回类型仍然是发送它的相同命令......带有一些添加状态。我想知道一个更有经验的函数式程序员会如何做到这一点。
  • Visual Studio 在我进行异常处理的每个地方都会给我一个警告。那有什么问题”它说

  • 这种类型测试或沮丧将始终保持

    我希望这段代码的样子是这样的

    让 x : MyRecord seq = GetConnection "con"|> GetCommand "cmd"|> AddParameter "@name"SqlDbType.NVarchar 50 |> AddParameter "@policyname"SqlDbType.NVarchar 50 |> ExecuteReader |> FunctionToReadAndGenerateSeq |> CleanEverything

    您能否建议我如何将我的代码提升到所需的水平以及任何其他改进?

    最佳答案

    我认为使用选项来表示失败的计算更适合纯函数式语言。在 F# 中,使用异常来表示计算失败是完全可以的。

    您的代码只是将异常转换为 None值,但它并没有真正处理这种情况 - 这留给您的代码调用者(谁需要决定如何处理 None )。您也可以让他们处理异常。如果您想向异常添加更多信息,您可以定义自己的异常类型并抛出它,而不是留下标准异常。

    下面定义了一个新的异常类型和一个简单的函数来抛出它:

    exception SqlUtilException of string

    // This supports the 'printf' formatting style
    let raiseSql fmt =
    Printf.kprintf (SqlUtilException >> raise) fmt

    使用简单的 .NET 样式并使用 F# 功能进行一些简化,代码看起来简单得多:
    // Using 'use' the 'Dispose' method is called automatically
    let connName = ConfigHandler.GetConnectionString "MyDB"
    use conn = new SqlConnection(connName)

    // Handle exceptions that happen when opening the connection
    try conn.Open()
    with ex -> raiseSql "Failed to connect to DB %s with Error %s " connName ex.Message

    // Using object initializer, we can nicely set the properties
    use cmd =
    new SqlCommand( Connection = conn, CommandText = "dbo.usp_MyStordProc",
    CommandType = CommandType.StoredProcedure )

    // Add parameters
    // (BTW: I do not think you need to set the type - this will be infered)
    let param1 = new SqlParameter("@User", SqlDbType.NVarChar, 50, Value = user)
    let param2 = new SqlParameter("@PolicyName", SqlDbType.NVarChar, 10, Value = policyName)
    cmd.Parameters.AddRange [| param1; param2 |]

    use reader =
    try cmd.ExecuteReader()
    with ex -> raiseSql "Failed to execute reader with error %s" ex.Message

    // Do more with the reader
    ()

    它看起来更像 .NET 代码,但这完全没问题。在 F# 中处理数据库将使用命令式风格并试图隐藏它只会使代码困惑。现在,您可以使用许多其他简洁的 F# 功能 - 特别是对动态运算符的支持 ? ,这会给你类似的东西:
    let connName = ConfigHandler.GetConnectionString "MyDB"

    // A wrapper that provides dynamic access to database
    use db = new DynamicDatabase(connName)

    // You can call stored procedures using method call syntax
    // and pass SQL parameters as standard arguments
    let rows = db.Query?usp_MyStordProc(user, policy)

    // You can access columns using the '?' syntax again
    [ for row in rows -> row?Column1, row?Column2 ]

    有关这方面的更多信息,请参阅以下 MSDN 系列:
  • How to: Dynamically Invoke a Stored Procedure
  • Step 1: Create a Database and Show the Poll Options
  • Step 2: Implement Voting for an Option
  • 关于f# - 数据库连接和 F#,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12556814/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com