Vp.FSharp.SqlPerpustakaan inti yang memungkinkan Anda bekerja dengan F# dan penyedia ADO apa pun, secara konsisten .
Dalam kebanyakan kasus, perpustakaan ini hanya digunakan untuk membuat perpustakaan F# lainnya yang memanfaatkan penyedia ADO yang relevan.
Jika Anda hanya ingin menjalankan perintah SQL A-la-F#, Anda mungkin ingin melihat bagian ini.
Kami mengikuti "praktik yang sangat kontroversial" dengan kemampuan terbaik kami!
| Status | Kemasan |
|---|---|
| OKE | |
| Ok (agak) | |
| Tbd | |
| Tbd |
| Nama | Versi | Memerintah |
|---|---|---|
Vp.FSharp.Sql | Install-Package Vp.FSharp.Sql |
Perpustakaan ini sebagian besar bertujuan untuk menjadi fondasi untuk membangun perpustakaan lain dengan penyedia ADO.NET yang relevan untuk memberikan pengalaman yang sangat diketik.
Anda dapat memeriksa perpustakaan di bawah ini, masing -masing memanfaatkan Vp.FSharp.Sql dan penyedia ADO.NET yang relevan:
| Nama | Penyedia ADO.NET | Versi | Memerintah |
|---|---|---|---|
Vp.FSharp.Sql.Sqlite | System.Data.SQLite.Core | Install-Package Vp.FSharp.Sql.Sqlite | |
Vp.FSharp.Sql.SqlServer | Microsoft.Data.SqlClient | Install-Package Vp.FSharp.Sql.SqlServer | |
Vp.FSharp.Sql.PostgreSql | Npgsql | Install-Package Vp.FSharp.Sql.PostgreSql |
Singkatnya, Anda dapat membuat penyedia lengkap Anda sendiri, tetapi Anda bebas hanya menggunakan hal -hal yang Anda butuhkan.
Mari kita berjalan-jalan dengan implementasi penyedia Vp.FSharp.Sql.Sqlite .
Pertama, Anda memerlukan tipe yang paling penting, tipe nilai database.
Dalam kasus SQLite, SqliteDbValue dapat dimodelkan sebagai Union Diskriminasi Sederhana (DU):
/// Native SQLite DB types.
/// See https://www.sqlite.org/datatype3.html
type SqliteDbValue =
| Null
| Integer of int64
| Real of double
| Text of string
| Blob of byte arraySumber Cuplikan | jangkar
Kasus -kasus ini dibuat setelah dokumentasi resmi SQLite.
Di sinilah kami mengonversi DU yang terpapar di API publik ke kelas DbParameter yang sebenarnya yang dapat dikonsumsi dari fungsi perpustakaan inti.
Dalam sebagian besar skenario, implementasi terdiri dari kecocokan pola penulisan pada kasus tipe nilai database yang berbeda dan membuat tipe spesifik DbParameter yang relevan yang tersedia di penyedia ADO.NET, jika ada:
let dbValueToParameter name value =
let parameter = SQLiteParameter ()
parameter.ParameterName <- name
match value with
| Null ->
parameter.TypeName <- ( nameof Null ) .ToUpperInvariant ()
| Integer value ->
parameter.TypeName <- ( nameof Integer ) .ToUpperInvariant ()
parameter.Value <- value
| Real value ->
parameter.TypeName <- ( nameof Real ) .ToUpperInvariant ()
parameter.Value <- value
| Text value ->
parameter.TypeName <- ( nameof Text ) .ToUpperInvariant ()
parameter.Value <- value
| Blob value ->
parameter.TypeName <- ( nameof Blob ) .ToUpperInvariant ()
parameter.Value <- value
parameterCatatan: Fungsi ini tidak harus publik, hanya DU yang harus dipublikasikan.
SqlDependencies bertindak seperti lem yang menempel semua operasi khusus ado yang paling penting:
/// SQLite Dependencies
type SqliteDependencies =
SqlDependencies <
SQLiteConnection ,
SQLiteCommand ,
SQLiteParameter ,
SQLiteDataReader ,
SQLiteTransaction ,
SqliteDbValue >Contoh jenis ini dapat diimplementasikan dengan:
let beginTransactionAsync ( connection : SQLiteConnection ) ( isolationLevel : IsolationLevel ) _ =
ValueTask.FromResult ( connection.BeginTransaction ( isolationLevel ))
let executeReaderAsync ( command : SQLiteCommand ) _ =
Task.FromResult ( command.ExecuteReader ())
{ CreateCommand = fun connection -> connection.CreateCommand ()
SetCommandTransaction = fun command transaction -> command.Transaction <- transaction
BeginTransaction = fun connection -> connection.BeginTransaction
BeginTransactionAsync = beginTransactionAsync
ExecuteReader = fun command -> command.ExecuteReader ()
ExecuteReaderAsync = executeReaderAsync
DbValueToParameter = Constants.DbValueToParameter } Dalam kasus khusus ini, System.Data.SQLite , jenis yang paling spesifik hanya tersedia melalui API non-sinkron.
Misalnya, kami menggunakan command.ExecuteReader alih -alih command.ExecuteDbDataReader karena tipe pengembalian adalah yang paling spesifik:
SQLiteCommand.ExecuteDbDataReader()SQLiteCommand.ExecuteReader()Juga, seperti yang mungkin Anda perhatikan tidak ada kejadian API asinkron, yang berarti bahwa "implementasi" asinkron (atau kurangnya) bergantung pada implementasi kelas dasar:
DbCommand.ExecuteReaderAsync()DbCommand.ExecuteDbDataReader(CommandBehavior behavior)yang hanya merupakan pembungkus asinkron di sekitar versi sinkron.
Demikian pula, ketika datang ke connection.BeginTransaction alih -alih command.BeginTransactionAsync :
SQLiteConnection.BeginTransaction()DbConnection.BeginTransactionAsync()Contoh ini saja menunjukkan jenis perbedaan yang dapat Anda harapkan dalam implementasi penyedia ADO.NET yang paling tersedia.
Demi kesederhanaan, Anda dapat membatasi tipe CommandDefinition dengan tipe penyedia ADO yang relevan, sebagai semacam pengikat jenis:
/// SQLite Command Definition
type SqliteCommandDefinition =
CommandDefinition <
SQLiteConnection ,
SQLiteCommand ,
SQLiteParameter ,
SQLiteDataReader ,
SQLiteTransaction ,
SqliteDbValue > Ini kemudian dapat digunakan dengan fungsi SqlCommand yang menerima CommandDefinition sebagai salah satu parameter mereka.
Ada spesialisasi lain dalam hal kendala generik:
/// SQLite Configuration
type SqliteConfiguration =
SqlConfigurationCache <
SQLiteConnection ,
SQLiteCommand >Jenis ini adalah pengikat lain untuk jenis dan bertindak sebagai cache; Ini akan dilewati bersama dengan definisi perintah saat menjalankan perintah.
Ini cukup mudah, yang perlu Anda lakukan adalah:
SqlCommand . [<RequireQualifiedAccess>]
module Vp.FSharp.Sql.Sqlite.SqliteCommand
open Vp. FSharp . Sql
/// Initialize a new command definition with the given text contained in the given string.
let text value : SqliteCommandDefinition =
SqlCommand.text value
/// Initialize a new command definition with the given text spanning over several strings (ie. list).
let textFromList value : SqliteCommandDefinition =
SqlCommand.textFromList value
/// Update the command definition so that when executing the command, it doesn't use any logger.
/// Be it the default one (Global, if any.) or a previously overriden one.
let noLogger commandDefinition = { commandDefinition with Logger = LoggerKind.Nothing }
/// Update the command definition so that when executing the command, it use the given overriding logger.
/// instead of the default one, aka the Global logger, if any.
let overrideLogger value commandDefinition = { commandDefinition with Logger = LoggerKind.Override value }
/// Update the command definition with the given parameters.
let parameters value ( commandDefinition : SqliteCommandDefinition ) : SqliteCommandDefinition =
SqlCommand.parameters value commandDefinition
/// Update the command definition with the given cancellation token.
let cancellationToken value ( commandDefinition : SqliteCommandDefinition ) : SqliteCommandDefinition =
SqlCommand.cancellationToken value commandDefinition
/// Update the command definition with the given timeout.
/// Note: kludged because SQLite doesn't support per-command timeout values.
let timeout value ( commandDefinition : SqliteCommandDefinition ) : SqliteCommandDefinition =
SqlCommand.timeout value commandDefinition
/// Update the command definition and sets whether the command should be prepared or not.
let prepare value ( commandDefinition : SqliteCommandDefinition ) : SqliteCommandDefinition =
SqlCommand.prepare value commandDefinition
/// Update the command definition and sets whether the command should be wrapped in the given transaction.
let transaction value ( commandDefinition : SqliteCommandDefinition ) : SqliteCommandDefinition =
SqlCommand.transaction value commandDefinitionDemikian juga, eksekusi perintah mengikuti prinsip-prinsip yang sama, alias melewati parameter yang sangat diketik (sesuai dengan penyedia ADO.NET Anda saat ini dan spesifik) dengan fungsi inti SQLCommand.
/// Execute the command and return the sets of rows as an AsyncSeq accordingly to the command definition.
/// This function runs asynchronously.
let queryAsyncSeq connection read ( commandDefinition : SqliteCommandDefinition ) =
SqlCommand.queryAsyncSeq
connection ( Constants.Deps ) ( SqliteConfiguration.Snapshot ) read commandDefinition
/// Execute the command and return the sets of rows as an AsyncSeq accordingly to the command definition.
/// This function runs synchronously.
let querySeqSync connection read ( commandDefinition : SqliteCommandDefinition ) =
SqlCommand.querySeqSync
connection ( Constants.Deps ) ( SqliteConfiguration.Snapshot ) read commandDefinition
/// Execute the command and return the sets of rows as a list accordingly to the command definition.
/// This function runs asynchronously.
let queryList connection read ( commandDefinition : SqliteCommandDefinition ) =
SqlCommand.queryList
connection ( Constants.Deps ) ( SqliteConfiguration.Snapshot ) read commandDefinition
/// Execute the command and return the sets of rows as a list accordingly to the command definition.
/// This function runs synchronously.
let queryListSync connection read ( commandDefinition : SqliteCommandDefinition ) =
SqlCommand.queryListSync
connection ( Constants.Deps ) ( SqliteConfiguration.Snapshot ) read commandDefinition
/// Execute the command and return the first set of rows as a list accordingly to the command definition.
/// This function runs asynchronously.
let querySetList connection read ( commandDefinition : SqliteCommandDefinition ) =
SqlCommand.querySetList
connection ( Constants.Deps ) ( SqliteConfiguration.Snapshot ) read commandDefinition
/// Execute the command and return the first set of rows as a list accordingly to the command definition.
/// This function runs synchronously.
let querySetListSync connection read ( commandDefinition : SqliteCommandDefinition ) =
SqlCommand.querySetListSync
connection ( Constants.Deps ) ( SqliteConfiguration.Snapshot ) read commandDefinition
/// Execute the command and return the 2 first sets of rows as a tuple of 2 lists accordingly to the command definition.
/// This function runs asynchronously.
let querySetList2 connection read1 read2 ( commandDefinition : SqliteCommandDefinition ) =
SqlCommand.querySetList2
connection ( Constants.Deps ) ( SqliteConfiguration.Snapshot ) read1 read2 commandDefinition
/// Execute the command and return the 2 first sets of rows as a tuple of 2 lists accordingly to the command definition.
/// This function runs synchronously.
let querySetList2Sync connection read1 read2 ( commandDefinition : SqliteCommandDefinition ) =
SqlCommand.querySetList2Sync
connection ( Constants.Deps ) ( SqliteConfiguration.Snapshot ) read1 read2 commandDefinition
/// Execute the command and return the 3 first sets of rows as a tuple of 3 lists accordingly to the command definition.
/// This function runs asynchronously.
let querySetList3 connection read1 read2 read3 ( commandDefinition : SqliteCommandDefinition ) =
SqlCommand.querySetList3
connection ( Constants.Deps ) ( SqliteConfiguration.Snapshot ) read1 read2 read3 commandDefinition
/// Execute the command and return the 3 first sets of rows as a tuple of 3 lists accordingly to the command definition.
/// This function runs synchronously.
let querySetList3Sync connection read1 read2 read3 ( commandDefinition : SqliteCommandDefinition ) =
SqlCommand.querySetList3Sync
connection ( Constants.Deps ) ( SqliteConfiguration.Snapshot ) read1 read2 read3 commandDefinition
/// Execute the command accordingly to its definition and,
/// - return the first cell value, if it is available and of the given type.
/// - throw an exception, otherwise.
/// This function runs asynchronously.
let executeScalar < 'Scalar > connection ( commandDefinition : SqliteCommandDefinition ) =
SqlCommand.executeScalar < 'Scalar , _, _, _, _, _, _, _, _>
connection ( Constants.Deps ) ( SqliteConfiguration.Snapshot ) commandDefinition
/// Execute the command accordingly to its definition and,
/// - return the first cell value, if it is available and of the given type.
/// - throw an exception, otherwise.
/// This function runs synchronously.
let executeScalarSync < 'Scalar > connection ( commandDefinition : SqliteCommandDefinition ) =
SqlCommand.executeScalarSync < 'Scalar , _, _, _, _, _, _, _, _>
connection ( Constants.Deps ) ( SqliteConfiguration.Snapshot ) commandDefinition
/// Execute the command accordingly to its definition and,
/// - return Some, if the first cell is available and of the given type.
/// - return None, if first cell is DBNull.
/// - throw an exception, otherwise.
/// This function runs asynchronously.
let executeScalarOrNone < 'Scalar > connection ( commandDefinition : SqliteCommandDefinition ) =
SqlCommand.executeScalarOrNone < 'Scalar , _, _, _, _, _, _, _, _>
connection ( Constants.Deps ) ( SqliteConfiguration.Snapshot ) commandDefinition
/// Execute the command accordingly to its definition and,
/// - return Some, if the first cell is available and of the given type.
/// - return None, if first cell is DBNull.
/// - throw an exception, otherwise.
/// This function runs synchronously.
let executeScalarOrNoneSync < 'Scalar > connection ( commandDefinition : SqliteCommandDefinition ) =
SqlCommand.executeScalarOrNoneSync < 'Scalar , _, _, _, _, _, _, _, _>
connection ( Constants.Deps ) ( SqliteConfiguration.Snapshot ) commandDefinition
/// Execute the command accordingly to its definition and, return the number of rows affected.
/// This function runs asynchronously.
let executeNonQuery connection ( commandDefinition : SqliteCommandDefinition ) =
SqlCommand.executeNonQuery
connection ( Constants.Deps ) ( SqliteConfiguration.Snapshot ) commandDefinition
/// Execute the command accordingly to its definition and, return the number of rows affected.
/// This function runs synchronously.
let executeNonQuerySync connection ( commandDefinition : SqliteCommandDefinition ) =
SqlCommand.executeNonQuerySync
connection ( Constants.Deps ) ( SqliteConfiguration.Snapshot ) commandDefinitionKami dapat membuat modul lain untuk pembantu nol, dan sisanya adalah tentang melewati parameter yang relevan ke fungsi inti yang mendasarinya.
[<RequireQualifiedAccess>]
module Vp.FSharp.Sql.Sqlite.SqliteNullDbValue
open Vp. FSharp . Sql
/// Return SQLite DB Null value if the given option is None, otherwise the underlying wrapped in Some.
let ifNone toDbValue = NullDbValue.ifNone toDbValue SqliteDbValue.Null
/// Return SQLite DB Null value if the option is Error, otherwise the underlying wrapped in Ok.
let ifError toDbValue = NullDbValue.ifError toDbValue ( fun _ -> SqliteDbValue.Null ) Lebih dari yang sama di sini juga.
[<RequireQualifiedAccess>]
module Vp.FSharp.Sql.Sqlite.SqliteTransaction
open Vp. FSharp . Sql
open Vp. FSharp . Sql . Sqlite
let private beginTransactionAsync = Constants.Deps.BeginTransactionAsync
let private beginTransaction = Constants.Deps.BeginTransaction
/// Create and commit an automatically generated transaction with the given connection, isolation,
/// cancellation token and transaction body.
/// This function runs asynchronously.
let commit cancellationToken isolationLevel connection body =
Transaction.commit cancellationToken isolationLevel connection beginTransactionAsync body
/// Create and commit an automatically generated transaction with the given connection, isolation,
/// and transaction body.
/// This function runs synchronously.
let commitSync isolationLevel connection body =
Transaction.commitSync isolationLevel connection beginTransaction body
/// Create and do not commit an automatically generated transaction with the given connection, isolation,
/// cancellation token and transaction body.
/// This function runs asynchronously.
let notCommit cancellationToken isolationLevel connection body =
Transaction.notCommit cancellationToken isolationLevel connection beginTransactionAsync body
/// Create and do not commit an automatically generated transaction with the given connection, isolation,
/// and transaction body.
/// This function runs synchronously.
let notCommitSync isolationLevel connection body =
Transaction.notCommitSync isolationLevel connection beginTransaction body
/// Create and commit an automatically generated transaction with the given connection, isolation,
/// cancellation token and transaction body.
/// The commit phase only occurs if the transaction body returns Some.
/// This function runs asynchronously.
let commitOnSome cancellationToken isolationLevel connection body =
Transaction.commitOnSome cancellationToken isolationLevel connection beginTransactionAsync body
/// Create and commit an automatically generated transaction with the given connection, isolation,
/// and transaction body.
/// The commit phase only occurs if the transaction body returns Some.
/// This function runs synchronously.
let commitOnSomeSync isolationLevel connection body =
Transaction.commitOnSomeSync isolationLevel connection beginTransaction body
/// Create and commit an automatically generated transaction with the given connection, isolation,
/// cancellation token and transaction body.
/// The commit phase only occurs if the transaction body returns Ok.
/// This function runs asynchronously.
let commitOnOk cancellationToken isolationLevel connection body =
Transaction.commitOnOk cancellationToken isolationLevel connection beginTransactionAsync body
/// Create and commit an automatically generated transaction with the given connection, isolation,
/// and transaction body.
/// The commit phase only occurs if the transaction body returns Ok.
/// This function runs synchronously.
let commitOnOkSync isolationLevel connection body =
Transaction.commitOnOkSync isolationLevel connection beginTransaction body
/// Create and commit an automatically generated transaction with the given connection and transaction body.
/// This function runs asynchronously.
let defaultCommit connection body = Transaction.defaultCommit connection beginTransactionAsync body
/// Create and commit an automatically generated transaction with the given connection and transaction body.
/// This function runs synchronously.
let defaultCommitSync connection body = Transaction.defaultCommitSync connection beginTransaction body
/// Create and do not commit an automatically generated transaction with the given connection and transaction body.
/// This function runs asynchronously.
let defaultNotCommit connection body = Transaction.defaultNotCommit connection beginTransactionAsync body
/// Create and do not commit an automatically generated transaction with the given connection and transaction body.
/// This function runs synchronously.
let defaultNotCommitSync connection body = Transaction.defaultNotCommitSync connection beginTransaction body
/// Create and commit an automatically generated transaction with the given connection and transaction body.
/// The commit phase only occurs if the transaction body returns Ok.
/// This function runs asynchronously.
let defaultCommitOnSome connection body = Transaction.defaultCommitOnSome connection beginTransactionAsync body
/// Create and commit an automatically generated transaction with the given connection and transaction body.
/// The commit phase only occurs if the transaction body returns Ok.
/// This function runs synchronously.
let defaultCommitOnSomeSync connection body = Transaction.defaultCommitOnSomeSync connection beginTransaction body
/// Create and commit an automatically generated transaction with the given connection and transaction body.
/// The commit phase only occurs if the transaction body returns Some.
/// This function runs asynchronously.
let defaultCommitOnOk connection body = Transaction.defaultCommitOnOk connection beginTransactionAsync body
/// Create and commit an automatically generated transaction with the given connection and transaction body.
/// The commit phase only occurs if the transaction body returns Some.
/// This function runs synchronously.
let defaultCommitOnOkSync connection body = Transaction.defaultCommitOnOkSync connection beginTransaction bodyDan voila! Anda sekarang semua diselesaikan dan siap untuk menjalankan perintah terliar terhadap database favorit Anda!
TransactionScope Helters Pembantu ini bekerja terlepas dari penyedia ADO.NET yang Anda gunakan selama mendukung TransactionScope .
Yang sedang berkata, kami sangat mencegah Anda menggunakan para pembantu itu :
TransactionScope (dengan atau tanpa pembantu) sangat rentan kesalahan, dan Anda mungkin menghadapi perilaku yang tidak terduga tanpa pesan kesalahan yang jelas.TransactionScope tanpa dukungan untuk transaksi terdistribusi, para pembantu mungkin pindah ke perpustakaan yang terpisah (Paket Repositori + Nuget).Jika Anda membutuhkan solusi yang layak untuk 2pc atau transaksi terdistribusi, Anda mungkin ingin memeriksa beberapa pola arsitektur seperti pola saga.
Laporan bug, permintaan fitur, dan permintaan tarik sangat disambut!
Harap baca pedoman kontribusi untuk memulai.
Proyek ini dilisensikan di bawah MIT.
Untuk informasi lebih lanjut tentang lisensi, lihat file lisensi.