F#异常处理多个“尝试”

时间:2015-09-18 23:37:29

标签: exception-handling f# try-catch

我正在尝试使用SQL Bulk Insert和DataContext.ExecuteCommand在SQL Server中读取一堆csv文件。 (也许这不是最好的方法,但它确实允许我留在Type Provider上下文 - 而不是我认为的SqlBulkCopy。)现在上传很麻烦,间歇性的成功。有些文件读入,有些文件因“数据转换错误(截断)”而失败。我认为这与行终止符并不总是有效。

当上传工作时,似乎是'0x0A'终止符。但是当失败时,我想再次尝试使用其他行终止符。所以我想进入一个Try语句,并且在失败时进入另一个Try语句,如果那个失败,则进入另一个,....这可能不是上传的最佳方式,但我仍然对它自己的状态的Try逻辑感到好奇。

这是我到目前为止所提出的并且它不太漂亮(但它有效)。切出几个嵌套层:

let FileRead path = 

    try
        db.DataContext.ExecuteCommand(@"BULK INSERT...ROWTERMINATOR='0x0A')") |> ignore 
        true
    with
        | exn -> 
            try
                db.DataContext.ExecuteCommand(@"BULK INSERT...ROWTERMINATOR='\r')") |> ignore 
                true
            with
                | exn -> 
                    try
                        db.DataContext.ExecuteCommand(@"BULK INSERT...ROWTERMINATOR='\n')") |> ignore 
                        true
                    with
                        | exn -> 
                            false

这感觉不对,但我还没有想出任何其他语法。

编辑:我最终做了什么,只是为了记录。欣赏被置于富有成效的道路上。在这方面有很多改进。有一个更重要的事情是使用Async并运行Parallel(我在其他部分已经获得了经验)。

type dbSchema = SqlDataConnection<dbConnection>
let db = dbSchema.GetDataContext() 

let TryUpLd table pathFile rowTerm = 
    try
        db.DataContext.ExecuteCommand( @"BULK INSERT " + table + " FROM '" + pathFile + 
                                       @"' WITH (FIELDTERMINATOR=',', FIRSTROW = 2, ROWTERMINATOR='" 
                                       + rowTerm + "')" ) |> ignore
        File.Delete (pathFile)  |> Some
    with
        | exn -> None

let NxtUpLd UL intOpt =
    match intOpt with
    | None -> UL
    | _ -> intOpt

let MoveTable ID table1 table2 = 
    //...
    ()

let NxtMoveTable MT intOpt = 
    match intOpt with
    | Some i -> MT
    | _ -> ()

let UpLdFile path (file:string) = 
    let (table1, table2) = 
        match path with
        | p when p = dlXPath   -> ("Data.dbo.ImportXs", "Data.dbo.Xs")
        | p when p = dlYPath -> ("Data.dbo.ImportYs", "Data.dbo.Ys")
        | _ -> ("ERROR path to tables", "")        

    let ID = file.Replace(fileExt, "")

    let TryRowTerm = TryUpLd table1 (path + file)

    TryRowTerm "0x0A" 
    |> NxtUpLd (TryRowTerm "\r")
    |> NxtUpLd (TryRowTerm "\n")
    |> NxtUpLd (TryRowTerm "\r\n")
    |> NxtUpLd (TryRowTerm "\n\r")
    |> NxtUpLd (TryRowTerm "\0")
    |> NxtMoveTable (MoveTable ID table1 table2) 


let UpLdData path = 
    let dir = new DirectoryInfo(path)
    let fileList = dir.GetFiles()

    fileList |> Array.iter (fun file -> UpLdFile path file.Name ) |> ignore

1 个答案:

答案 0 :(得分:6)

这是使用monadic组合的一种方法。

首先,定义一个将另一个函数作为输入的函数,但将任何异常转换为None值:

let attempt f =
    try f () |> Some
    with | _ -> None

此函数的类型为(unit -> 'a) -> 'a option;即:f被推断为将unit作为输入并返回值的任何函数。如您所见,如果没有异常发生,则调用f的返回值将包含在Some个案例中。 attempt函数会抑制所有异常,you shouldn't normally do

接下来,定义此attemptNext函数:

let attemptNext f = function
    | Some x -> Some x
    | None -> attempt f

此函数的类型为(unit -> 'a) -> 'a option -> 'a option。如果输入'a optionSome,则只返回。换句话说,该值被解释为已经成功,因此没有理由尝试下一个函数。

否则,如果输入'a optionNone,则会将其解释为上一步导致失败。在这种情况下,使用f函数尝试输入函数attempt

这意味着您现在可以一起组合函数,并获得第一个成功的结果。

以下是一些要测试的功能:

let throwyFunction () = raise (new System.InvalidOperationException("Boo"))
let throwyFunction' x y = raise (new System.InvalidOperationException("Hiss"))
let goodFunction () = "Hooray"
let goodFunction' x y = "Yeah"

在F#Interactive中试用它们:

> let res1 =
    attempt throwyFunction
    |> attemptNext (fun () -> throwyFunction' 42 "foo")
    |> attemptNext goodFunction
    |> attemptNext (fun () -> goodFunction' true 13.37);;

val res1 : string option = Some "Hooray"

> let res2 =
    attempt goodFunction
    |> attemptNext throwyFunction
    |> attemptNext (fun () -> throwyFunction' 42 "foo")
    |> attemptNext (fun () -> goodFunction' true 13.37);;

val res2 : string option = Some "Hooray"

> let res3 =
    attempt (fun () -> throwyFunction' 42 "foo")
    |> attemptNext throwyFunction    
    |> attemptNext (fun () -> goodFunction' true 13.37)
    |> attemptNext goodFunction;;

val res3 : string option = Some "Yeah"

> let res4 =
    attempt (fun () -> throwyFunction' 42 "foo")
    |> attemptNext (fun () -> goodFunction' true 13.37)
    |> attemptNext throwyFunction    
    |> attemptNext goodFunction;;

val res4 : string option = Some "Yeah"