使用F#TypeProviders处理来自两个不同服务器但数据库的SQL数据。它现在很好,除了现在的速度变得非常慢,因为我添加了更多的文件。截至目前,每个文件都有自己的连接字符串。我正在考虑将连接字符串和类型提供程序提取到外部项目,并能够将数据库作为参数传递。为了实现这一点,我在以下链接F# Type Provider, create with string variable的第3个回答后尝试以下内容:
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>
但一直收到错误:
这不是有效的常量表达式
是否也可以使用ChangeDatabase
中的SqlDataConnection
成员?
更新 在这种情况下,建议的答案不会解决我的问题。以下是与两个不同服务器的不同表格相似的内容:
[<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"
现在我需要为每个表执行此操作,即每个表的类以及文件增长编译以及intellisense启动需要这么长时间。
答案 0 :(得分:6)
您尝试采取的方法存在根本性缺陷。您希望从应用程序配置在运行时获取连接字符串,并提供SqlDataConnection
类型提供程序,以便与底层数据库结合使用。
但是这种类型的提供者在工作流的运行时阶段根本无法做任何事情,因为它的工作必须已经在编译时的编译时数据库上完成 。
然后,您可能会问,如果我们想在编译一次后创建代码,使用类型提供程序有什么意义,能够使用在运行时配置的数据库吗?
是的,但我们确实希望类型提供商工作的结果适用于结构上相同的数据库,对吗?
因此,出路是提供类型提供程序在数据库compileTimeDB
上完成其工作,字面在编译时连接字符串compileTimeCC
已知,并考虑所有好东西我们在连接字符串上获取(类型检查,智能感知,...)参数化。此连接字符串参数值runTimeCC
可以在运行时以任何所需方式设置,只要它指向具有与runTimeDB
相同模式的数据库compileTimeDB
。
用下面的一些代码说明这个基本原理:
[<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
类型OldPersonT1
类型OldPersonT2
和ModernPerson
作为数据源并将一个当代localdb
类型作为目标进行限制。我在这里谈论类型,它可以在您的数据库场周围有多少实例。
现在,让我们在名为CompileTypeDB
的{{1}}创建一个数据库并运行Sql脚本,以创建与OldPersonT1
,OldPersonT2
和{相对应的表格{1}}(这是一次性练习,不涉及真正的数据移动)。这将是ModernPerson
TP的单一信息来源。
准备好之后,让我们回到代码:
SqlDataConnection
然后,使用以下静态成员扩充每个遗留类型(仅为简洁起见,下面针对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
为了使这个工作实时DB可能与编译时DB不同,它应该包含OldPersonT1.MakeModernPersons("real time connection string to any DB having OldPersonT1 table")
具有和依赖的所有内容。
同样适用于OldPersonT1
或任何其他变体类型:通过为每种变体类型实施OldPersonT2
,您可以获得所有数据源实例。
使用数据目标需要一个带签名的单个函数
MakeModernPersons
现在仅通过操纵两个实时连接字符串的值来涵盖let makeModernPersons destinationConnStr (source: IQueryable<ModernPerson>) =
...
数据源和目标的所有可能组合。
非常粗略,但这个想法似乎很清楚。