2017-03-15 10 views
2

私のF#プログラムはSQL Serverと対話する必要があります。F#パラメータを使用してSQLコマンドを設定する最善の方法

let workFlowDetailRuncommand = new SqlCommand(query, econnection) 
    workFlowDetailRuncommand.CommandTimeout <- 100000 
    workFlowDetailRuncommand.Parameters.Add("@1", SqlDbType.Int).Value <- 42 
    workFlowDetailRuncommand.Parameters.Add("@2", SqlDbType.VarChar).Value <- "answer" 
    workFlowDetailRuncommand.Parameters.Add("@3", SqlDbType.VarChar).Value <- mydate.ToString("yyyy.MM.dd") 
    workFlowDetailRuncommand.Parameters.Add("@4", SqlDbType.VarChar).Value <- "D. Adams" 
    workFlowDetailRuncommand.Parameters.Add("@5", SqlDbType.DateTime).Value <- DateTime.Now 
    workFlowDetailRuncommand.Parameters.Add("@6", SqlDbType.Text).Value <- filename 

これを行うために、よりidomatic方法があり、このような時にパラメータ1を設定せず(少ないタイピングでは!):1つの部分では、私はこのようなものを持っています。

+2

ここでSqlCommandProviderを見てください。あなたのユースケースに完全に適合しないかもしれませんが(特にクエリが動的である場合)、パラメータをうまく処理します:http://fsprojects.github.io/FSharp.Data.SqlClient/ –

答えて

2

これはテストしていません。

ヘルパー機能をいくつか作成します。

let createSqlCommand query connection = 
    new SqlCommand(query, connection) 

let setTimeout timeout (sqlCommand: SqlCommand) = 
    sqlCommand.CommandTimeout <- timeout 
    sqlCommand 

let addInt name (value: int) (sqlCommand: SqlCommand) = 
    sqlCommand.Parameters.Add(name, SqlDbType.Int).Value <- value 
    sqlCommand 

このように使用します。

let mySqlCommand = 
    createSqlCommand someQuery someConnection 
    |> setTimeout 100000 
    |> addInt "@1" 41 
    |> addString "@s" "Hello" 
    |> addDateTime "@dt1" DateTime.Now 
    |> addFloat "@f1" 5.1 

intがある場合は、常にSqlDbType.Intを使用するのが理にかなっています。

しかし、文字列がある場合は、フィールドタイプの候補がいくつかあります。そのため、addXxx関数の名前にF#/ .NET型ではなくフィールド型を反映させることをお勧めします。あなたはaddVarChar、addNVarChar、addCharなど

|> addInt "@i1" myInt 
|> addDateTime "@dt1" myDateTime 
|> addText "@tagText" myTagText 
|> addNVarChar "@letter" myLetterBody 
4

を作成しているので、私は、ベントからの答えはあなたの標準SqlCommandオブジェクトを構築するための非常に素晴らしいDSLを与えると思います。それはあなたが必要とするものである可能性が非常に高いかもしれません - もしあなたがいくつかのコマンドを作成するためのより良い構文を望むならば、それは完全に動作します。

SQLコマンドでさらに多くのことを行いたい場合、DSLには基本的な変更可能なSqlCommandタイプに基づいているという1つの制限があります。機能的に見えますが、あなたを困らせるかもしれません。

より包括的なオプションは、ドメインをキャプチャするために、独自の機能タイプを定義することです - つまり、あなたが実行したいクエリの種類:

type Parameter = 
    | Int of int 
    | VarChar of string 
    | Text of string 
    | DateTime of System.DateTime 

type Command = 
    { Query : string 
    Timeout : int 
    Parameters : (string * Parameter) list } 

は、その後、あなたは(通常のF#の型を使用してクエリを構築することができますまだ不変のもの)を維持しながら、あなたも、ベントは、この上に示唆したようなDSLを実現することができます。

let cmd = 
    { Query = query 
    Timeout = 100000 
    Parameters = 
     [ "@1", Int 42 
     "@2", VarChar "answer" 
     "@3", VarChar (mydate.ToString("yyyy.MM.dd")) 
     "@4", VarChar "D. Adams" 
     "@5", DateTime DateTime.Now 
     "@6", Text filename ] } 

最後のビットは、コマンドおよび接続を取り、にそれを回す関数を記述することですSqlCommand

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 
    sql 

何最善のアプローチであることはあなたのユースケースに依存します - とデータベースと対話することは、本質的に不純なので、おそらく孤立し、不純なものを維持することは完全に罰金です。より機能的でドメインに集中したい場合は、これを可能な選択肢として表示したいと思います(F#のドメインドリブンモデリング!)。

+1

私はこのテクニックを使って巨大なF#の種類に基づくXMLの量は素晴らしく機能しました。 –

+1

これは素晴らしいです!私はそれを使い始めている。 1つのフォローアップの質問。 SQLConnectionはDisposeをサポートしているので、 '' 'use connection = new SqlConnection(connString)' ''これをどのようにしてこの考えに適合させるのですか? – user1443098

+1

@ user1443098これは、 'Command'型に' SqlConnection'を含まない理由です。つまり、コマンドを独立して設定し、 'use'を使って接続を開き、' createSqlCommand'を呼び出してSQLコマンドを実行することができます - 接続を閉じます。まだ不可欠な部分がありますが、それは最後まで押し込まれます。 –

関連する問題