2012-09-23 6 views
5

私はF#でのSQLServerにStoredProcを実行するには、次のコードを書いたデータベース接続と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 

私は何よりも、このコード

  1. まず第Option.bindを繰り返し使用すると、複数の問題を抱えています非常に刺激的です...とノイズを追加しています。出力が「なし」であるかどうかを確認するために、より明確な方法が必要です。

  2. 最後に、読者、コマンド、接続をクローズ+ディスパッチできるクリーンアップ機能が必要です。しかし現在、パイプラインの終わりには私が持っているものはすべて読者です。

  3. パラメータを追加している関数...戻り値の型がまだ送信されたコマンドと同じであるため、コマンドパラメータの "状態"が変更されているようです。私は経験豊富な機能プログラマがこれをどのようにしたのだろうと思います。

  4. Visual Studioでは、例外処理を行う場所ごとに警告が表示されます。 CON 『:それと間違っているものを」MyRecord SEQ =のgetConnectionそれは

このタイプのテストやダウンキャストは常に

私はこのコードを見てみたい道はこの

レットxが保持すると言います』 |> GetCommand "CMD" |> AddParameter "@name" SqlDbType.NVarchar 50 |> AddParameter "@policyname" SqlDbType.NVarchar 50 |>のExecuteReader |> FunctionToReadAndGenerateSeq |> CleanEverything

あなたは、私は私のコードを取ることができますどのようにお勧めします所望のレベルに、また他の改善?

答えて

7

私は、失敗した計算を表すためにオプションを使用することは、純粋に機能的なランゲージに適していると思います。 F#では、計算が失敗したことを示すために例外を使用するのはまったく問題ありません。

コードでは例外がNoneの値に変わりますが、実際にはこのような状況は処理されません。これはコードの呼び出し側に任せられます(Noneと何をするか決定する必要があります)。例外を処理できるようにすることもできます。例外にさらに情報を追加したい場合は、独自の例外タイプを定義し、標準の例外を残す代わりにそれをスローすることができます。

以下はそれをスローする新しい例外タイプと簡単な関数を定義しています

exception SqlUtilException of string 

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

F#の機能を使用して、いくつかの単純化とプレーンな.NETのスタイルを使用して、コードがずっと簡単になります。

// 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シリーズを参照してください

関連する問題