gpt4 book ai didi

F# TypeProviders,如何更改数据库?

转载 作者:行者123 更新时间:2023-12-01 18:19:50 26 4
gpt4 key购买 nike

我使用 F# TypeProviders 处理来自两个不同服务器但许多数据库的 SQL 数据。一切进展顺利,除了速度现在随着我添加更多文件而变得非常慢。目前每个文件都有自己的连接字符串。我正在考虑将连接字符串和类型提供程序提取到外部项目,并能够将数据库作为参数传递。为了实现这一目标,我尝试执行以下操作,遵循以下链接 F# Type Provider, create with string variable 中的第三个答案。 :

open System.Configuration
open System.Data.SqlClient
open Microsoft.FSharp.Data.TypeProviders
open System.Data.SqlClient

let con = ConfigurationManager.AppSettings.Item("conDebug")
let setDB dbName =
let cn = new SqlConnectionStringBuilder(con)
cn.InitialCatalog <- dbName
cn.ConnectionString

[<Literal>]
let conStr = setDB "AnotherDB"

type selectedDb = SqlDataConnection<conStr>

但不断收到错误:这不是有效的常量表达式是否也可以在 SqlDataConnection 中使用 ChangeDatabase 成员?

更新:在这种情况下,建议的答案无法解决我的问题。下面是类似于对来自两个不同服务器的不同表所做的操作:

[<Literal>]
let sourceConStr = @"sourceCon"

type sourceDB = SqlDataConnection<sourceConStr>

type person =
{ Id : string
birthDate : Nullable<DateTime>
firstName : string
lastName : string
dateCreated : DateTime }

type processPeople()
member this.makePerson(oldPerson: source.ServiceTypes.Person) =
{ Id = oldPerson.oldId
......some other processing of old data
}

member this.transferPeople conStr =
use source = sourceDB.GetDataContext()
use sw = File.AppendText("./ImportLog.txt")
use targetCon = new SqlConnection(conStr)
targetCon.Open()
let query = "insert query"

let insert person =
try
use cmd = new SqlCommand(query, targetCon)
....setting parameters stuff......
cmd.ExecuteNonQuery() |> ignore
with sx -> sw.WriteLine(sx.Message)
source.source.ServiceTypes.Person
|> Seq.map this.makePerson
|> Seq.filter (fun p -> p.Id <> null && p.birthDate.HasValue )
|> Seq.iter insert
printfn "Done"

现在我需要对每个表(即每个表的类)执行此操作,并且随着文件的增长,编译和智能感知启动需要很长时间。

最佳答案

您尝试采用的方法存在根本缺陷。您希望在运行时从应用程序配置中获取连接字符串并提供 SqlDataConnection输入提供程序以利用底层数据库发挥其魔力。

但是这种类型提供程序根本无法在工作流的运行时阶段执行任何操作,因为其工作必须在某些编译时已知数据库上在编译时完成。

那么,您可能会问,如果我们想让我们的代码在编译一次后能够在运行时配置数据库,那么使用类型提供程序有什么意义呢?

是的,但我们确实希望类型提供程序工作的结果适用于结构相同的数据库,是吗?

因此,出路是提供类型提供程序来在数据库上完成其工作 compileTimeDB在编译时连接字符串字面上已知 compileTimeCC ,并考虑我们在连接字符串上参数化获得的所有好处(类型检查、智能感知等)。此连接字符串参数值runTimeCC可以在运行时以任何所需的方式设置,只要它指向数据库 runTimeDBcompileTimeDB 具有相同的架构.

用下面的一些代码来说明这一基本原理:

[<Literal>]
let compileTimeCC = @"Data Source=(localdb)\ProjectsV12;Initial Catalog=compileTimeDB;Integrated Security=True;"
.....
type MySqlConnection = SqlDataConnection<ConnectionString = compileTimeCC>
// Type provider is happy, let it help us writing our DB-related code
.....
let db = MySqlConnection.GetDataContext(runTimeCC)
// at run time db will be a runTimeDB set by connection string runTimeCC that can be any
// as long as runTimeDB and compileTimeDB has same schema
.....

更新随着问题作者使他的问题背景更加清晰,我可能会在使用给定的 TP 解决此问题时提出更具体的建议。由于SO答案应该相当简洁,让我们限制两个遗留的考虑Person类型OldPersonT1OldPersonT2作为数据源和当代人ModernPerson输入目的地。我在这里讨论的是类型,您的数据库场周围可以有任意数量的此类实例

现在,让我们在 localdb 创建一个数据库命名CompileTypeDB并运行Sql脚本来创建OldPersonT1对应的表, OldPersonT2 ,和ModernPerson (这是一次性练习,不会涉及真正的数据移动)。这将是 SqlDataConnection 的类型信息的单一来源。 TP。

准备好后,让我们回到代码:

type CTSqlConn = SqlDataConnection<ConnectionString = @"Data Source=(LocalDB)\Projectsv12;Initial Catalog=myCompileTimeDB;Integrated Security=True">

type OldPersonT1 = CTSqlConn.ServiceTypes.OldPersonT1 // just for brevity
type OldPersonT2 = CTSqlConn.ServiceTypes.OldPersonT2
type ModernPerson = CTSqlConn.ServiceTypes.ModernPerson

然后,使用以下静态成员来扩充每个旧类型(为了简洁起见,下面给出 OldPersonT1):

type CTSqlConn.ServiceTypes.OldPersonT1 with
static member MakeModernPersons(rtConn: string) =
let projection (old: OldPersonT1) =
// Just a direct copy, but you may be very flexible in spreading verification
// logic between query, projection, and even makeModernPersons function
// that will be processing IQueryable<ModernPerson>
let mp = ModernPerson()
mp.Id <- old.Id
mp.birthDate <- old.birthDate
mp.firstName <- old.firstName
mp.lastName <- old.lastName
mp.dateCreated <- old.dateCreated
mp
query {
for oldPerson in (CTSqlConn.GetDataContext(rtConn)).OldPersonT1 do
select (projection oldPerson)
}

现在您可以联系IQueryable<ModernPerson>来自 OldPersonT1 类型的任何数据源仅通过评估

OldPersonT1.MakeModernPersons("real time connection string to any DB having OldPersonT1 table")

要使其工作,实时数据库可能与编译时数据库不同,它应该包含 OldPersonT1 的所有内容。具有并取决于。OldPersonT2 也是如此。或任何其他变体类型:通过实现 MakeModernPersons对于每个变体类型,您可以获得覆盖的所有数据源实例。

处理数据目标需要一个带有签名的函数

let makeModernPersons destinationConnStr (source: IQueryable<ModernPerson>) =
...

现在涵盖了 Person 的所有可能组合只需通过操作两个实时连接字符串的值即可确定数据源和目标。

剪裁非常粗糙,但想法似乎很清晰。

关于F# TypeProviders,如何更改数据库?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32312503/

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