F#异常处理多个“ Tries” [英] F# exception handling multiple "Tries"
问题描述
我正在尝试使用SQL Bulk Insert和DataContext.ExecuteCommand在SQL Server中读取一堆csv文件。 (也许这不是最好的方法,但它确实允许我停留在Type Provider上下文中-与我认为的SqlBulkCopy相对。)现在,上传失败并间歇性地成功。某些文件已读入,有些则失败并显示数据转换错误(截断)。我认为这与行终止符不总是有效有关。
I'm trying to read a bunch of csv files in SQL Server using SQL Bulk Insert and DataContext.ExecuteCommand. (Maybe this isn't the best way to do it, but it does allow me stay in the Type Provider context--as opposed to with SqlBulkCopy I think.) Now the upload is glitchy with intermittent success. Some files read in, some fail with "Data conversion error (truncation)". I think this has to do with the row terminators not always working.
上传成功后,似乎使用了 0x0A终止符。但是,如果失败了,我想与其他行终止符一起再次尝试。因此,我想进入一个Try语句,并在失败时进入另一个Try语句,如果失败则另一个......。这可能不是最好的上传方式,但是我仍然对它自己的状态的尝试逻辑感到好奇。
When the upload works, it seems to be with the '0x0A' terminator. But when that fails, I want to try repeatedly again with other row terminators. So I want to go into a Try statement, and on failure go into another Try statement, and another if that one fails, ... . This may not be the best way to upload, but I am still curious about the Try logic for it's own state.
到目前为止,这是我要提出的内容,虽然不是很漂亮(但可以)。删除一些嵌套层:
Here's what I've come up with so far and it's not too pretty (but it works). Cutting out a few nested layers:
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
这感觉不对,但我还没有弄清楚其他语法。
This doens't feel right but I haven't figured out any other syntax.
编辑:我最终所做的只是为了记录。赞赏被置于生产性道路上。有很多可以改进的地方。更重要的事情之一就是使用Async并使其并行运行(我在其他部分已经获得了经验)。
What I ended up doing, just for the record. Appreciate being put on a productive path. There's plenty to improve in this. With one of the more significant things being to use Async's and run it Parallel (which I have gotten experience with in other sections).
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
推荐答案
这是使用单调组合的一种方法
Here's one way to do it, using monadic composition.
首先,定义一个将另一个函数作为输入但将任何异常转换为 None
值的函数:
First, define a function that takes another function as input, but converts any exception to a None
value:
let attempt f =
try f () |> Some
with | _ -> None
此函数的类型为(unit->'a)- >一个选项
;即: f
推断为以 unit
作为输入并返回值的任何函数。如您所见,如果没有异常发生,则调用 f
的返回值将包装在 Some
情况下。 尝试
函数可抑制所有您通常不应该这样做。
This function has the type (unit -> 'a) -> 'a option
; that is: f
is inferred to be any function that takes unit
as input, and returns a value. As you can see, if no exception happens, the return value from invoking f
is wrapped in a Some
case. The attempt
function suppresses all exceptions, which you shouldn't normally do.
下一步,定义此 attemptNext
函数:
let attemptNext f = function
| Some x -> Some x
| None -> attempt f
此函数的类型为(unit->'a) ->一个选项->一个选项
。如果输入的’选项
是一些
,则只需返回。换句话说,该值被解释为已经成功,因此没有理由尝试下一个函数。
This function has the type (unit -> 'a) -> 'a option -> 'a option
. If the input 'a option
is Some
then it's simply returned. In other words, the value is interpreted as already successful, so there's no reason to try the next function.
否则,如果输入'a选项
为无
,这被解释为好像上一步导致失败。在这种情况下,尝试使用 attatt
函数输入函数 f
。
Otherwise, if the input 'a option
is None
, this is interpreted as though the previous step resulted in a failure. In that case, the input function f
is attempted, using the attempt
function.
这意味着您现在可以将函数组合在一起,并获得第一个成功的结果。
This means that you can now compose functions together, and get the first successful result.
以下是一些可以测试的函数:
Here are some functions to test with:
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中尝试一下:
Try them out in 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"
这篇关于F#异常处理多个“ Tries”的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!