首页 > 解决方案 > 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>

应用程序配置

<?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类型提供程序的一些东西。我会很感激任何建议。

标签: f#fsharp.data.sqlclient

解决方案


首先是最明显的错误: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类型的附加列
  • 如果这很重要,请在其上添加一个非聚集索引
  • GUID通过生成一个新模型来开始创建每个新模型System.Guid.NewGuid()
  • 随心所欲地使用此 GUID 值,然后当您准备好持久化模型时,使用 SQL DML对字段INSERT使用相同的 GUIDmodelUid

请注意,通过这种方法,您也只使用类型提供程序SqlCommandProviderFSharp.Data.SqlClient类型,因此事情保持简单。


推荐阅读