F#异常处理多个"尝试"

Rom*_*eEE 2 f# exception-handling 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
Run Code Online (Sandbox Code Playgroud)

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

编辑:我最终做了什么,只是为了记录.欣赏被置于富有成效的道路上.在这方面有很多改进.有一个更重要的事情是使用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
Run Code Online (Sandbox Code Playgroud)

Mar*_*ann 6

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

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

let attempt f =
    try f () |> Some
    with | _ -> None
Run Code Online (Sandbox Code Playgroud)

这个功能有类型(unit -> 'a) -> 'a option; 即:f推断为任何unit作为输入的函数,并返回一个值.如您所见,如果没有异常发生,则调用的返回值f将包装在一个Somecase中.该attempt函数会抑制您通常不应该执行的所有异常.

接下来,定义此attemptNext功能:

let attemptNext f = function
    | Some x -> Some x
    | None -> attempt f
Run Code Online (Sandbox Code Playgroud)

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

否则,如果输入'a option是,则将其None解释为前一步骤导致失败.在这种情况下,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"
Run Code Online (Sandbox Code Playgroud)

在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"
Run Code Online (Sandbox Code Playgroud)