Exception handling F“异常处理多个”;“尝试”;

Exception handling F“异常处理多个”;“尝试”;,exception-handling,f#,try-catch,Exception Handling,F#,Try Catch,我正在尝试使用SQL批量插入和DataContext.ExecuteCommand在SQL Server中读取一组csv文件。(也许这不是最好的方法,但它确实允许我停留在类型提供程序上下文中——我认为这与使用SqlBulkCopy不同。)现在,上传有了间歇性的成功。有些文件被读入,有些文件因“数据转换错误(截断)”而失败。我认为这与行终止符不总是起作用有关 当上传工作时,它似乎与“0x0A”终止符在一起。但如果失败,我想用其他行终止符再次尝试。所以我想进入一个Try语句,在失败时进入另一个Try

我正在尝试使用SQL批量插入和DataContext.ExecuteCommand在SQL Server中读取一组csv文件。(也许这不是最好的方法,但它确实允许我停留在类型提供程序上下文中——我认为这与使用SqlBulkCopy不同。)现在,上传有了间歇性的成功。有些文件被读入,有些文件因“数据转换错误(截断)”而失败。我认为这与行终止符不总是起作用有关

当上传工作时,它似乎与“0x0A”终止符在一起。但如果失败,我想用其他行终止符再次尝试。所以我想进入一个Try语句,在失败时进入另一个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
这感觉不对,但我还没有想出任何其他语法

编辑:我最后所做的,只是为了记录在案。欣赏被放在一条富有成效的道路上。这方面还有很多需要改进的地方。其中一个更重要的事情是使用异步并并行运行它(我在其他章节中也有过这方面的经验)

类型dbSchema=SqlDataConnection
设db=dbSchema.GetDataContext()
让TryUpLd表路径文件行项=
尝试
db.DataContext.ExecuteCommand(@“大容量插入”+表+”来自“+”路径文件+
@“'WITH(FIELDTERMINATOR=',',FIRSTROW=2,rowtterminator='”
+rowTerm+“')”))|>忽略
File.Delete(pathFile)|>一些
具有
|exn->None
让NxtUpLd UL插入=
匹配
|无->UL
|_uu->intOpt
让MoveTable ID table1 table2=
//...
()
让NxtMoveTable MT intOpt=
匹配
|一些我->MT
| _ -> ()
让上传文件路径(文件:字符串)=
let(表1、表2)=
匹配路径
|p=dlXPath->(“Data.dbo.ImportXs”、“Data.dbo.Xs”)
|p=dlYPath->(“Data.dbo.ImportYs”、“Data.dbo.Ys”)
|->(“表的错误路径”,“”)
让ID=file.Replace(fileExt,“”)
让TryRowTerm=TryUpLd表1(路径+文件)
TryRowTerm“0x0A”
|>NxtUpLd(TryRowTerm“\r”)
|>NxtUpLd(TryRowTerm“\n”)
|>NxtUpLd(TryRowTerm“\r\n”)
|>NxtUpLd(TryRowTerm“\n\r”)
|>NxtUpLd(TryRowTerm“\0”)
|>NxtMoveTable(移动表ID表1表2)
让UpLdData路径=
let dir=新目录信息(路径)
让fileList=dir.GetFiles()
fileList |>Array.iter(乐趣文件->上传文件路径文件.Name)|>ignore

这里有一种方法,使用一元合成

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

let attempt f =
    try f () |> Some
    with | _ -> None
此函数具有类型
(单元->“a”->“a”选项
;也就是说:
f
被推断为以
unit
作为输入并返回值的任何函数。如您所见,如果没有发生异常,调用
f
的返回值将被包装在
Some
案例中。
trunt
函数将抑制所有异常,这会导致异常

接下来,定义此
attemptNext
函数:

let attemptNext f = function
    | Some x -> Some x
    | None -> attempt f
此函数的类型为
(单位->'a)->'a option->'a option
。如果输入的
'a选项
Some
,则只返回它。换句话说,该值被解释为已经成功,因此没有理由尝试下一个函数

否则,如果输入的
'a选项
,这将被解释为上一步导致了故障。在这种情况下,使用
trunt
功能尝试输入功能
f

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

以下是一些要测试的函数:

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"

也许只是打个电话?听起来不错。让我试试这样的版本。谢谢。不过我还是很好奇,是否会有这样一种情况:先尝试a,然后尝试B,然后…,直到一个成功——而替代方案并没有像我的情况那样将它们用于简单替换的循环。一个代码怎么可能一直尝试到没有失败。非常感谢,马克。这太棒了。到目前为止,我只读过单子,所以我很高兴终于尝试将其中的一些想法付诸实践。再次感谢。我应该等一等,然后再标记这个答案,以防万一还有更多的讨论继续。那我回头再查。再次感谢。
> 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"