F # SqlProvider не может обновить изменения в файле DBF dBase с подключением ODBC - PullRequest
0 голосов
/ 18 мая 2018

У меня есть следующий код 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:

Сообщение -> ОШИБКА [HY092] [Microsoft] [Драйвер ODBC dBase] Неверный идентификатор атрибута / опции Источник -> odbcjt32.dll

Есть ли способ заставить его работать или мне нужно отказаться от провайдера типов и вернуться к OleDb?

UPDATE

Отключение транзакций делает вещи немного лучше, теперь ошибка вызвана отсутствием первичного ключа в файлах dbf, с которыми мне приходится работать.

Единственный измененный код - получение контекста данных

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

И новая ошибка:

System.Exception: Ошибка - вы не можете обновить объект, который не есть первичный ключ. (dbo.variable) в FSharp.Data.Sql.Providers.OdbcProvider.createUpdateCommand (IDbConnection con, StringBuilder sb, объект SqlEntity, FSharpList`1 changeColumns)

at. $ Providers.Odbc.FSharp-Data-Sql-Common-ISqlProvider-ProcessUpdates @ 648-4.Invoke (SqlEntity есть Microsoft.FSharp.Collections.SeqModule.Iterate [T] (источник FSharpFunc 2 action, IEnumerable 1) в FSharp.Data.Sql.Providers.OdbcProvider.FSharp-данных Sql-Common-ISqlProvider-ProcessUpdates (IDbConnection con, ConcurrentDictionary 2 entities, TransactionOptions transactionOptions, FSharpOption 1 тайм-аут) в . $ SqlRuntime.DataContext.f @ 1-69 (SqlDataContext __, IDbConnection con, Unit unitVar0) в FSharp.Data.Sql.Runtime.SqlDataContext.FSharp-Data-Sql-Common-ISqlDataContext-SubmitPendingChanges ()

Есть идеи, как бороться с этим пробемом?

1 Ответ

0 голосов
/ 18 мая 2018

Я нашел хитрый / грязный способ, который я бы классифицировал скорее как обходной путь, чем как реальное решение, но он работает в моем случае;поэтому я буду использовать его, если / пока кто-то еще не предложит окончательный вариант.

Чтобы заставить поставщика типов работать, мне нужно сделать 2 вещи не в обычном рабочем процессе:

  1. Контекст данных должен быть извлечен с отключенными транзакциями
  2. Перед выполнением операций изменения в DBF я создаю первичный ключ для этого DBF с помощью оператора SQL более низкого уровня

Здесь работаеткод

[<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 ()
...