我使用F#TypeProviders处理来自两个不同服务器但很多数据库的SQL数据.它现在很好,除了现在的速度变得非常慢,因为我添加了更多的文件.截至目前,每个文件都有自己的连接字符串.我正在考虑将连接字符串和类型提供程序提取到外部项目,并能够将数据库作为参数传递.为了实现这一点,我尝试以下内容,在以下链接F#Type Provider的第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>
Run Code Online (Sandbox Code Playgroud)
但继续得到错误:
这不是一个有效的常量表达式
是否也可以使用该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"
Run Code Online (Sandbox Code Playgroud)
现在我需要为每个表执行此操作,即每个表的类和文件增长编译以及intellisense启动需要这么长时间.
您尝试采取的方法存在根本缺陷.您希望在运行时从应用程序配置中获取连接字符串,并提供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
.....
Run Code Online (Sandbox Code Playgroud)
更新作为问题作者使他的问题背景更清楚我可以建议在给定TP时接近这个更具体的建议.由于SO答案应该相当简洁,因此我们将两种遗留Person类型OldPersonT1和OldPersonT2数据源以及一种当代ModernPerson类型作为目的地进行限制.我在这里谈论类型,它可以是你想要的数据库农场周围的实例.
现在,让我们为您创建一个单独的数据库localdb命名CompileTypeDB和创建对应于表运行SQL脚本OldPersonT1,OldPersonT2以及ModernPerson(这是一个时间的锻炼,也没有真正的数据移动将参与).这将是SqlDataConnectionTP 的单一信息来源.
准备好之后,让我们回到代码:
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
Run Code Online (Sandbox Code Playgroud)
然后,使用以下静态成员扩充每个遗留类型(以下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)
}
Run Code Online (Sandbox Code Playgroud)
现在,您只需通过评估即可IQueryable<ModernPerson>从任何类型OldPersonT1的数据源中获取
OldPersonT1.MakeModernPersons("real time connection string to any DB having OldPersonT1 table")
Run Code Online (Sandbox Code Playgroud)
为了实现这一点,实时DB可能与编译时DB不完全相同,它只应包含OldPersonT1具有和依赖的所有内容.对于OldPersonT2或任何其他变体类型也是如此:通过MakeModernPersons为每个变体类型实现一次,您将获得所有数据源实例.
使用数据目标需要一个带签名的功能
let makeModernPersons destinationConnStr (source: IQueryable<ModernPerson>) =
...
Run Code Online (Sandbox Code Playgroud)
现在Person,只需操作两个实时连接字符串的值,即可涵盖数据源和目标的所有可能组合.
非常粗略,但这个想法似乎很清楚.