我正在尝试使用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
答案 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 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"
在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"