2012-09-23 16 views
5

मैं एफ # में एक 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 

मैं इस कोड

  1. सबसे पहले Option.bind का बार-बार इस्तेमाल के साथ कई समस्याएं हैं बहुत परेशान है ... और शोर जोड़ रहा है। मुझे यह जांचने के लिए एक और स्पष्ट तरीका चाहिए कि आउटपुट कोई नहीं था और यदि नहीं तो आगे बढ़ें।

  2. अंत में एक क्लीनअपफंक्शन होना चाहिए जहां मुझे पाठक, कमांड और कनेक्शन को बंद करने में सक्षम होना चाहिए। लेकिन वर्तमान में पाइपलाइन के अंत में मेरे पास पाठक है।

  3. फ़ंक्शन जो पैरामीटर जोड़ रहा है ... ऐसा लगता है कि यह कमांड पैरामीटर के "स्थिति" को संशोधित कर रहा है क्योंकि रिटर्न प्रकार अभी भी वही कमांड है जिसे इसे भेजा गया था ... कुछ अतिरिक्त राज्य के साथ। मुझे आश्चर्य है कि एक और अनुभवी कार्यात्मक प्रोग्रामर ने यह कैसे किया होगा।

  4. विजुअल स्टूडियो मुझे उस स्थान पर एक चेतावनी देता है जहां मैं अपवाद हैंडलिंग करता हूं। क्या उस के साथ गलत क्या है "यह कहता है

इस प्रकार का परीक्षण या खिन्न हमेशा का आयोजन करेगा

तरह से मैं इस कोड को देखने के लिए चाहते हैं तो इस

देना एक्स है: MyRecord seq = GetConnection" चोर " |> GetCommand "cmd" |> AddParameter "@name" SqlDbType.NVarchar 50 |> AddParameter "@policyname" SqlDbType.NVarchar 50 |> ExecuteReader |> FunctionToReadAndGenerateSeq |> CleanEverything

आप की सिफारिश कर सकते हैं कि मैं अपने कोड ले जा सकते हैं वांछित स्तर और किसी भी अन्य के लिए सुधार की?

उत्तर

7

मुझे लगता है कि असफल गणनाओं का प्रतिनिधित्व करने के विकल्पों का उपयोग करना पूरी तरह से कार्यात्मक लैंगुग के लिए अधिक उपयुक्त है। एफ # में, यह गणना करने के लिए अपवादों का उपयोग करना बिल्कुल ठीक है कि एक गणना विफल हो गई है।

आपका कोड बस None मानों में अपवाद बदलता है, लेकिन यह वास्तव में इस स्थिति को संभाल नहीं करता है - यह आपके कोड के कॉलर को छोड़ दिया गया है (जिसे यह तय करने की आवश्यकता होगी कि None के साथ क्या करना है)। आप उन्हें अपवाद को संभालने दे सकते हैं। यदि आप अपवाद में अधिक जानकारी जोड़ना चाहते हैं, तो आप अपने अपवाद प्रकार को परिभाषित कर सकते हैं और मानक अपवादों को छोड़ने के बजाय इसे फेंक सकते हैं।

निम्नलिखित फेंक करने के लिए कोई नया अपवाद प्रकार और एक साधारण समारोह को परिभाषित करता है:

exception SqlUtilException of string 

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

एफ # सुविधाओं का उपयोग कर कुछ सरलीकरण के साथ सादे नेट शैली का उपयोग करना, कोड बहुत आसान लग रहा है:

// 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 कोड की तरह दिखता है, लेकिन यह बिल्कुल ठीक है। एफ # में डेटाबेस के साथ काम करना अनिवार्य शैली का उपयोग करने जा रहा है और छिपाने की कोशिश कर रहा है जो केवल कोड को भ्रमित कर देगा।अब, वहाँ आप इस्तेमाल कर सकते हैं अन्य स्वच्छ एफ # सुविधाओं की एक संख्या है - विशेष रूप से गतिशील ऑपरेटरों ? के लिए समर्थन है, जो आप की तरह कुछ देना होगा:

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 श्रृंखला इस बारे में अधिक जानकारी के लिए देखें:

संबंधित मुद्दे