F#-FSharp.Data.SqlClient-获取身份值后更新行

时间:2019-01-02 21:48:17

标签: f# fsharp.data.sqlclient

假设我具有以下条件:

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>

我需要从表ModelData中获取SQL身份值。它在代码中的某处使用。因此,我具有以下功能,以使用一些默认值添加新行,然后返回标识值。

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 :(得分:0)

从最明显的错误开始: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的记录,为什么不做:

  • 引入类型为uniqueidentifier的附加列modelUid
  • 在此上面添加非聚集索引
  • 通过使用GUID生成新的System.Guid.NewGuid()来创建每个新模型
  • 根据需要使用此GUID值,然后在准备持久化模型时使用SQL DML INSERTmodelUid字段使用相同的GUID

请注意,通过这种方法,您也只使用了SqlCommandProvider类型的FSharp.Data.SqlClient类型的提供程序,因此事情变得简单了。