F#SqlProvider无法使用ODBC连接更新dBase DBF文件中的更改

时间:2018-05-17 23:23:02

标签: f# odbc dbf dbase

我有以下F#代码

open FSharp.Data.Sql
open FSharp.Data.Sql.Runtime
open System.IO

[<Literal>]
let private schemaConn = @"Driver={Microsoft dBASE Driver (*.dbf)};DriverID=277;Dbq=C:\Citect\User\NPM;" 
type private schema = SqlDataProvider<Common.DatabaseProviderTypes.ODBC, schemaConn>
let private connStringFormat = Printf.StringFormat<string->string>(@"Driver={Microsoft dBASE Driver (*.dbf)};DriverID=277;Dbq=%s;")

type internal Project = {
    name : string
    path : string
    dcx : schema.dataContext
}

[<Literal>]
let private cUserPath = @"C:\Citect\User"

let private findPath projectName = 
    Directory.GetDirectories(cUserPath, projectName, SearchOption.AllDirectories)
    |> Array.find (fun d -> d.Contains("web") |> not)

let internal connect projectName =
    let path' = findPath projectName
    let connString = sprintf connStringFormat path'
    let dcx' = schema.GetDataContext(connString)
    { name = projectName; path = path'; dcx = dcx' }

let internal updVariable (project : Project) variable = 
    let dcx = project.dcx
    let q = query {
        for v in dcx.Dbo.Variable do
            where (v.Addr = "%MW217.0")
            select v
            exactlyOne
    }
    q.Addr <- "QQQ"
    dcx.SubmitUpdates() //error

let internal prj = connect "NPMUG_SCC35"
updVariable prj ()

连接和查询按预期工作,但是当我尝试更新数据源时,我收到来自odbc驱动程序的以下错误:

  

消息 - &gt;错误[HY092] [Microsoft] [ODBC dBase驱动程序]无效   属性/选项标识符来源 - &gt; ODBCJT32.DLL

有没有办法让它工作或者我是否需要放弃类型提供者并回归OleDb?

更新

禁用事务使事情变得更好,现在错误是由于我必须使用的dbf文件中缺少主键。

唯一改变的代码是获取数据上下文

let dcx = schema.GetDataContext( { Timeout = TimeSpan.MaxValue; IsolationLevel = Transactions.IsolationLevel.DontCreateTransaction } : FSharp.Data.Sql.Transactions.TransactionOptions)

新错误是:

  

System.Exception:错误 - 您无法更新没有的实体   有一个主键。 (dbo.variable)at   FSharp.Data.Sql.Providers.OdbcProvider.createUpdateCommand(的IDbConnection   con,StringBuilder sb,SqlEntity实体,FSharpList`1 changedColumns)

     
    

at。$ Providers.Odbc.FSharp-Data-Sql-Common-ISqlProvider-ProcessUpdates @ 648-4.Invoke(SqlEntity)     吃     Microsoft.FSharp.Collections.SeqModule.Iterate [T](FSharpFunc 2 action, IEnumerable 1 source)at at     FSharp.Data.Sql.Providers.OdbcProvider.FSharp-DATA-SQL的共ISqlProvider-ProcessUpdates(的IDbConnection     con,ConcurrentDictionary 2 entities, TransactionOptions transactionOptions, FSharpOption 1超时)at     。$ @ SqlRuntime.DataContext.f 1-69(SqlDataContext     __,IDbConnection con,Unit unitVar0)at FSharp.Data.Sql.Runtime.SqlDataContext.FSharp-Data-Sql-Common-ISqlDataContext-SubmitPendingChanges()

  

有关如何处理此探测器的任何想法吗?

1 个答案:

答案 0 :(得分:1)

我发现了一种棘手/肮脏的方式,我将其分类为一种解决方法,而不是真正的解决方案,但它适用于我的情况;所以我将使用它,除非/直到其他人建议一个结论性的。

为了让类型提供者工作,我需要做两件事,而不是在通常的工作流程中:

  1. 需要在禁用交易的情况下检索数据上下文
  2. 在对DBF执行更改操作之前,我创建了一个主数据库 使用较低级别的SQL语句键入该DBF
  3. 这里是工作代码

    [<Literal>]
    let private schemaConn = @"Driver={Microsoft dBASE Driver (*.dbf)};DriverID=277;Dbq=C:\Citect\User\NPM;READONLY=FALSE" 
    type private schema = SqlDataProvider<Common.DatabaseProviderTypes.ODBC, schemaConn>
    let private connStringFormat = Printf.StringFormat<string->string>(@"Driver={Microsoft dBASE Driver (*.dbf)};DriverID=277;Dbq=%s;READONLY=FALSE")
    
    type internal Project = {
        name : string
        path : string
        dcx : schema.dataContext
    }
    
    [<Literal>]
    let private cUserPath = @"C:\Citect\User"
    
    let private findPath projectName = 
        Directory.GetDirectories(cUserPath, projectName, SearchOption.AllDirectories)
        |> Array.find (fun d -> d.Contains("web") |> not)
    
    let private createPK (cn : IDbConnection) = 
        let cm = cn.CreateCommand()
        cm.CommandText <- "ALTER TABLE Variable ADD PRIMARY KEY (Name)"
        try
            cn.Open()
            cm.ExecuteNonQuery() |> ignore
        finally cn.Close()
    
    let internal connect projectName =
        let path' = findPath projectName
        let connString = sprintf connStringFormat path'
        let transOptions = { Timeout = TimeSpan.FromSeconds(3.0); IsolationLevel = Transactions.IsolationLevel.DontCreateTransaction }
        let dcx' = schema.GetDataContext(connectionString = connString, transactionOptions = transOptions)
        dcx'.CreateConnection() |> createPK
        { name = projectName; path = path'; dcx = dcx' }
    
    let internal updVariable (project : Project) variable = 
        let dcx = project.dcx
        let q = query {
            for v in dcx.Dbo.Variable do
                where (v.Addr = "%MW217.0")
                select v
                exactlyOne
        }
        q.Addr <- "QQQ"
        dcx.SubmitUpdates()
    
    let internal prj = connect "NPMUG_SCC35"
    updVariable prj ()