F # - FSharp.Data.SqlClient - обновить строку после получения значения идентификатора - PullRequest
0 голосов
/ 03 января 2019

Допустим, у меня есть следующее:

SQL

Создайте базу данных с именем Clm, затем запустите этот скрипт:

CREATE TABLE dbo.ModelData(
    modelId bigint IDENTITY(1,1) NOT NULL,
    numberOfAminoAcids int NOT NULL,
    maxPeptideLength int NOT NULL,
    seed int NOT NULL,
    fileStructureVersion nvarchar(50) NOT NULL,
    modelData nvarchar(max) NOT NULL,
    createdOn datetime NOT NULL,
 CONSTRAINT PK_ModelData PRIMARY KEY CLUSTERED 
(
    modelId ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON PRIMARY
) ON PRIMARY TEXTIMAGE_ON PRIMARY
GO
ALTER TABLE dbo.ModelData ADD  CONSTRAINT DF_ModelData_createdOn  DEFAULT (getdate()) FOR createdOn
GO

F #

[<Literal>]
let ClmDbName = "Clm"

[<Literal>]
let AppConfigFile = __SOURCE_DIRECTORY__ + "\.\App.config"

[<Literal>]
let ClmConnectionString = "Server=localhost;Database=" + ClmDbName + ";Integrated Security=SSPI"

[<Literal>]
let ClmSqlProviderName = "name=" + ClmDbName

type ClmDB = SqlProgrammabilityProvider<ClmSqlProviderName, ConfigFile = AppConfigFile>
type ModelDataTable = ClmDB.dbo.Tables.ModelData
type ModelDataTableRow = ModelDataTable.Row

type ModelDataTableData = 
    SqlCommandProvider<"select * from dbo.ModelData where modelId = @modelId", ClmConnectionString, ResultType.DataReader>

App.config

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
  </startup>
  <connectionStrings configSource="db.config" />
  <runtime>
    <gcAllowVeryLargeObjects enabled="true" />
  </runtime>
</configuration>

db.config

<connectionStrings>
    <add name="Clm" connectionString="Data Source=localhost;Initial Catalog=Clm;Integrated Security=SSPI" />
</connectionStrings>

Мне нужно получить значение идентификатора SQL из таблицы ModelData.Он используется где-то в коде.Итак, у меня есть следующая функция, чтобы добавить новую строку с некоторыми значениями по умолчанию и затем вернуть значение идентификатора.

let getNewModelDataId (conn : SqlConnection) =
    let t = new ModelDataTable()
    let r = 
        t.NewRow(
                numberOfAminoAcids = 0,
                maxPeptideLength = 0,
                seed = 0,
                fileStructureVersion = "",
                modelData = "",
                createdOn = DateTime.Now
                )

    t.Rows.Add r
    t.Update(conn) |> ignore
    r.modelId

let openConnIfClosed (conn : SqlConnection) =
    match conn.State with
    | ConnectionState.Closed -> do conn.Open()
    | _ -> ignore ()

И я использую его, чтобы получить новое значение идентификатора modelId из базы данных.

let modelId = getNewModelDataId conn

Через примерно 0,5 - 1,5 часа времени выполнения мне нужно обновить некоторые данные, например,

use d = new ModelDataTableData(conn)
let t1 = new ModelDataTable()
d.Execute(modelId = modelId) |> t1.Load
let r1 = t1.Rows |> Seq.find (fun e -> e.modelId = modelId)
r1.modelData <- "Some new data..."
t1.Update(conn) |> ignore

, где строка "Some new data..." представляет некоторую довольно большую строку.Это происходит только один раз за modelId.Код выше работает .Но это выглядит так уродливо, особенно часть t1.Rows |> Seq.find ... ☹ Я думаю, что я что-то упускаю из провайдеров типа FSharp.Data.SqlClient.Буду признателен за любой совет.

1 Ответ

0 голосов
/ 04 января 2019

Начнем с самой очевидной ошибки: FSharp.Data.SqlClient провайдер типа через SqlCommandProvider поддерживает любые операторы изменения данных DML, включая UPDATE. Так, вместо всего этого джаза с переносом модели на клиентскую сторону, изменением и возвратом на сервер, то же самое можно достичь с помощью

...
use cmd = SqlCommandProvider<
              "UPDATE [dbo].[ModelData] SET [modelData]=@ModelData WHERE [modelId]=@ModelId",
               conn>(conn)
cmd.Execute(ModelData="Some new data...", ModelId=modelId) |> ignore
...

Менее очевидная проблема связана с использованием identity field. Похоже, у него нет особой роли, кроме уникальной идентификации каждой новой модели. Таким образом, вместо того, чтобы возиться с созданием поддельной записи, затем извлекать ее id с сервера, а затем обновлять запись, имеющую эту id, с реальными значениями, почему бы просто:

  • ввести дополнительный столбец modelUid типа uniqueidentifier
  • добавить некластеризованный индекс, если это имеет значение
  • начинайте создавать каждую новую модель, генерируя свежие GUID с System.Guid.NewGuid()
  • используйте это значение GUID как хотите, затем, когда вы будете готовы сохранить свою модель, сделайте это с помощью SQL DML INSERT, используя тот же GUID для modelUid поле

Обратите внимание, что при таком подходе вы также используете только SqlCommandProvider типа провайдера FSharp.Data.SqlClient типа, так что все остается простым.

...