2012-09-23 30 views
5

tôi đã viết đoạn code sau đây để thực hiện một StoredProc SQLServer trong F #Database Connections và F #

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 

Tôi có nhiều vấn đề với mã này

  1. Trước hết việc sử dụng lặp đi lặp lại của Option.bind rất khó chịu ... và đang thêm tiếng ồn. Tôi cần một cách rõ ràng hơn để kiểm tra xem đầu ra là Không và nếu không thì tiến hành.

  2. Cuối cùng, cần có một chức năng dọn dẹp nơi tôi có thể đóng + hủy bỏ trình đọc, lệnh và kết nối. Nhưng hiện tại ở cuối đường ống tất cả những gì tôi có là người đọc.

  3. Chức năng đang thêm tham số ... có vẻ như nó đang sửa đổi "trạng thái" của tham số lệnh vì kiểu trả về vẫn là lệnh được gửi ... với một số trạng thái được thêm vào. Tôi tự hỏi làm thế nào một lập trình viên chức năng có kinh nghiệm hơn sẽ làm điều này.

  4. Visual Studio cung cấp cho tôi cảnh báo ở từng nơi tôi xử lý ngoại lệ. có gì sai với điều đó" nó nói

thử nghiệm loại này hoặc nhìn xuống sẽ luôn giữ

Con đường tôi muốn mã này nhìn là

let x này: MyRecord seq = getConnection 'con' Quay lại đầu trang | đến mức mong muốn và bất kỳ cấp độ nào khác cải tiến?

Trả lời

7

Tôi nghĩ rằng việc sử dụng các tùy chọn để thể hiện tính toán không thành công phù hợp hơn với các ngôn ngữ thuần túy chức năng. Trong F #, nó là hoàn toàn tốt để sử dụng ngoại lệ để biểu thị rằng một tính toán đã thất bại.

Mã của bạn chỉ đơn giản là biến ngoại lệ thành None giá trị, nhưng nó không thực sự xử lý tình huống này - đây là để người gọi mã của bạn (người sẽ cần phải quyết định làm gì với None). Bạn cũng có thể cho phép họ xử lý ngoại lệ. Nếu bạn muốn thêm thông tin khác vào ngoại lệ, bạn có thể xác định loại ngoại lệ của riêng mình và ném loại trừ thay vì để lại ngoại lệ tiêu chuẩn.

Sau đây định nghĩa một loại ngoại lệ mới và một chức năng đơn giản để ném nó:

exception SqlUtilException of string 

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

Sử dụng phong cách đồng bằng NET với một vài đơn giản hóa sử dụng F # tính năng, mã trông đơn giản hơn rất nhiều:

// 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 
() 

Dường như mã .NET, nhưng điều đó hoàn toàn ổn. Xử lý các cơ sở dữ liệu trong F # sẽ sử dụng kiểu bắt buộc và cố giấu nó sẽ chỉ làm cho mã khó hiểu.Bây giờ, có một số F # tính năng khác gọn gàng bạn có thể sử dụng - đặc biệt là sự hỗ trợ cho các nhà khai thác động ?, mà sẽ cung cấp cho bạn một cái gì đó như:

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 ] 

Để biết thêm thông tin về vấn đề này, xem loạt MSDN sau đây: