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)
这是使用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 option是Some那么它只是返回.换句话说,该值被解释为已经成功,因此没有理由尝试下一个函数.
否则,如果输入'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)