Let’s say that I have the following:
SQL
Create database called Clm
, then run this script:
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>
I need to get SQL identity value from table ModelData
. It is used somewhere in the code. So, I have the following function to add a new row with some default values and then get identity value back.
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 ()
And the I use it to get new identity value of modelId
from the database.
let modelId = getNewModelDataId conn
The after about 0.5 – 1.5 hours of execution time I need to update some data, e.g.
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
where the string "Some new data..."
represents some fairly large string. This only happens once per modelId
.
The code above does work. But it looks soooo ugly, epsecially the part t1.Rows |> Seq.find ...
☹ I guess that I am missing something about FSharp.Data.SqlClient
type providers. I’d appreciate any advice.
To begin with the most obvious blunder:
FSharp.Data.SqlClient
type provider via itsSqlCommandProvider
supports any DML data modification statements, includingUPDATE
. So, instead of all that jazz with pulling the model to the client side, modifying, and pushing back to the server, the same can be achieved byThe less obvious problem relates to the use of
identity field
. It sounds like it does not have any special role beyond uniquely identifying every new model. So, instead of the tinkering with creating a fake record, then pulling itsid
from the server, then updating the record having thisid
with real values, why not just:modelUid
of type uniqueidentifierGUID
withSystem.Guid.NewGuid()
INSERT
using the same GUID formodelUid
fieldNotice, that with such approach you also use just
SqlCommandProvider
type ofFSharp.Data.SqlClient
type provider, so the things stay simple.