gpt4 book ai didi

ado.net - F# 中的 Haskell HDBC 优雅?

转载 作者:行者123 更新时间:2023-12-02 18:10:33 31 4
gpt4 key购买 nike

Haskell 的简洁和优雅给我留下了深刻的印象。但我在 .Net 公司工作,所以当我可以使用 F# 时我会使用它——我可能是全国数百个使用它的人中唯一的一个。

ADO.NET 或 F# 是否提供像 HDBC 的 executeMany 一样简洁而优雅的功能?我正在通过Real World Haskell 。在 chapter 21它提供了这个例子:

ghci> conn <- connectSqlite3 "test1.db"
ghci> stmt <- prepare conn "INSERT INTO test VALUES (?, ?)"
ghci> executeMany stmt [[toSql 5, toSql "five's nice"], [toSql 6, SqlNull]]
ghci> commit conn
ghci> disconnect conn

我希望在我的 F# 中获得这种优雅和简洁。我见过很多关于使用参数化查询来避免 SQL 注入(inject)攻击的宣传。在这种情况下,我没有使用它们有以下三个原因:

  1. 我发现 .Net 中的参数化查询丑陋且繁重。
  2. 我的数据来自公司办公室,因此(大部分)是干净的。
  3. 我的表格有 34 列。我鄙视用 34 列参数化查询的想法。

这是我的 F# 代码:

module Data

open System
open System.Data
open System.Data.OleDb
open System.Text.RegularExpressions

type Period = Prior | Current

let Import period records db =
use conn = new OleDbConnection(@"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + db + ";Persist Security Info=False;")

let execNonQuery s =
let comm = new OleDbCommand(s, conn) in
comm.ExecuteNonQuery() |> ignore

let enquote = sprintf "\"%s\""
let escapeQuotes s = Regex.Replace(s, "\"", "\"\"")
let join (ss:string[]) = String.Join(",", ss)

let table = match period with
| Prior -> "tblPrior"
| Current -> "tblCurrent"
let statements =
[| for r in records do
let vs = r |> Array.map (escapeQuotes >> enquote) |> join
let vs' = vs + sprintf ",\"14\",#%s#" (DateTime.Now.ToString "yyyy-MM-dd") in
yield sprintf "INSERT INTO %s ( [Field01], [Field02], [Field03] [Field04], [Field05], [Field06], [Field07], [Field08], [Field09], [Field10], [Field11], [Field12], [Field13], [Field14], [Field15], [Field16], [Field17], [Field18], [Field19], [Field20], [Field21], [Field22], [Field23], [Field24], [Field25], [Field26], [Field27], [Field28], [Field29], [Field30], [Field31], [Field32], [Field33], [Field34] ) VALUES (%s)" table vs' |] in

do conn.Open()
execNonQuery (sprintf "DELETE FROM %s" table)
statements |> Array.iter execNonQuery

出于安全原因,我已重命名表的字段。

因为表中的所有字段都是文本,所以我可以轻松地对它们进行 Array.map 来转义并引用值。

每天将 9,000 到 10,000 条记录导入到两个表中,我希望尽可能高效地完成此操作。因此我对 Haskell 的 executeMany 感兴趣。不过,我也喜欢参数化查询背后的想法,也喜欢 Hasekll 实现它们的方式。 F# 中是否有类似的简洁和优雅的东西?

最佳答案

我同意 @JonnyBoats 的评论,即通常使用 F# SQL 类型提供程序,如 SqlDataConnection (LINQ 到 SQL)或 SqlEntityConnection ( Entity Framework )比任何涉及手动构建插入语句字符串的解决方案都要优雅得多。

但是,您的问题有一个重要的限定条件:“每天将 9,000 到 10,000 条记录导入到两个表中,我希望尽可能高效地完成此操作。”在这种情况下,您需要使用 SqlBulkCopy用于高效的批量插入(它利用 native 数据库驱动程序功能实现比 HDBC 的 executeMany 更快的插入速度)。

这是一个小示例,可以帮助您开始使用 SqlBulkCopy与 F#:https://stackoverflow.com/a/8942056/236255 。请注意,您将使用 DataTable在我看来,暂存数据虽然很旧并且在 F# 中使用起来有些尴尬,但仍然优于构建插入语句字符串。

针对评论进行更新

这是使用 SqlBulkCopy 的通用方法这针对您的场景进行了改进(我们与行数据分开传递列规范,并且两者都是动态的):

//you must reference System.Data and System.Xml
open System
open System.Data
open System.Data.SqlClient

let bulkLoad (conn:SqlConnection) tableName (columns:list<string * Type>) (rows: list<list<obj>>) =
use sbc = new SqlBulkCopy(conn, SqlBulkCopyOptions.TableLock, null, BatchSize=500, BulkCopyTimeout=1200, DestinationTableName=tableName)
sbc.WriteToServer(
let dt = new DataTable()
columns
|> List.iter (dt.Columns.Add>>ignore)

for row in rows do
let dr = dt.NewRow()
row |> Seq.iteri(fun i value -> dr.[i] <- value)
dt.Rows.Add(dr)
dt)

//example usage:

//note: since you know all your columns are of type string, you could define columns like
//let columns = ["Field1", "Field2", "Field3"] |> List.map (fun name -> name, typeof<String>)
let columns = [
"Field1", typeof<String>
"Field2", typeof<String>
"Field3", typeof<String>
]

let rows = [
["a"; "b"; "c"]
["d"; "e"; "f"]
["g"; "h"; "i"]
["j"; "k"; "l"]
["m"; "n"; "o"]
]

//a little funkiness to transform our list<list<string>> to list<list<obj>>,
//probably not needed in practice because you won't be constructing your lists literally
let rows = rows |> List.map (fun row -> row |> List.map (fun value -> value :> obj))

bulkLoad conn "tblPrior" columns rows

使用涉及反射的方法,您可以变得更精美/更简洁。例如创建一个类似

的类型
type RowData = { Field1:string; Field2:string; Field3:string }

并创建一个bulkLoad签名为 list<'a>参数,使其反射(reflect) typeof<'a> 的属性名称和类型构建DataTable Columns ,并类似地使用反射来迭代行实例的所有属性,以创建新行并将其添加到 DataTable 。事实上,this question展示如何制作通用 ToDataTable执行此操作的方法(在 C# 中)。

关于ado.net - F# 中的 Haskell HDBC 优雅?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16321601/

31 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com