Vp.FSharp.SqlF#およびADOプロバイダーを一貫して作業できるようにするコアライブラリ。
ほとんどの場合、このライブラリは、関連するADOプロバイダーを活用する他のF#ライブラリの作成にのみ使用されます。
SQLコマンドをA-LA-F#を実行するだけの場合は、このセクションをご覧ください。
私たちは「非常に物議を醸す慣行」に従って、私たちの能力を最大限に発揮します!
| 状態 | パッケージ |
|---|---|
| わかりました | |
| わかりました(ソート) | |
| TBD | |
| TBD |
| 名前 | バージョン | 指示 |
|---|---|---|
Vp.FSharp.Sql | Install-Package Vp.FSharp.Sql |
このライブラリは、主に、関連するADO.NETプロバイダーを使用して他のライブラリを構築するための基盤となることを目指しており、強力な体験を提供します。
以下のライブラリを確認できます。それぞれがVp.FSharp.Sqlと関連するADO.NETプロバイダーを活用しています。
| 名前 | ADO.NETプロバイダー | バージョン | 指示 |
|---|---|---|---|
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 |
一言で言えば、独自の完全なプロバイダーを作成できますが、必要なものだけで自由に行くことができます。
Vp.FSharp.Sql.Sqliteプロバイダーの実装をウォークスルーしましょう。
まず、すべての最も重要なタイプであるデータベース値タイプが必要です。
SQLiteの場合、 SqliteDbValue単純な差別化された連合(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 arrayスニペットソース|アンカー
これらのケースは、公式のSQLiteドキュメントの後に作成されます。
これは、パブリックAPIで露出したDUを、コアライブラリ関数から消費できる実際のDbParameter compatibleクラスに変換する場所です。
ほとんどのシナリオでは、実装は、さまざまなデータベース値タイプのケースでパターンマッチを記述し、ADO.NETプロバイダーで利用可能な関連するDbParameter固有のタイプを作成することで構成されています。
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
parameter注:この機能は公開する必要はなく、DUだけが公開されなければなりません。
SqlDependencies 、最も重要なすべての基礎となるADO固有の操作を固執する接着剤のように機能します。
/// SQLite Dependencies
type SqliteDependencies =
SqlDependencies <
SQLiteConnection ,
SQLiteCommand ,
SQLiteParameter ,
SQLiteDataReader ,
SQLiteTransaction ,
SqliteDbValue >このタイプのインスタンスは、次のように実装できます
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 }この特定のケースでは、 System.Data.SQLite 、最も具体的なタイプは、非同期APIによってのみ利用可能です。
たとえば、 command.ExecuteDbDataReaderの代わりにcommand.ExecuteReaderを使用します。
SQLiteCommand.ExecuteDbDataReader()SQLiteCommand.ExecuteReader()また、あなたが気づいたかもしれませんが、非同期APIの発生はありません。つまり、非同期の「実装」(またはその欠如)が基本クラスの実装に依存していることを意味します。
DbCommand.ExecuteReaderAsync()DbCommand.ExecuteDbDataReader(CommandBehavior behavior)これは、同期バージョンの周りの非同期ラッパーです。
同様に、 connection.BeginTransactionの代わりにcommand.BeginTransactionAsyncになると、begintransactionasync:
SQLiteConnection.BeginTransaction()DbConnection.BeginTransactionAsync()この例だけで、最も利用可能なADO.NETプロバイダーの実装で見つけることが期待できる矛盾の種類を示しています。
簡単にするために、関連するADOプロバイダータイプを使用して、 CommandDefinitionタイプを何らかのタイプバインダーとして制約できます。
/// SQLite Command Definition
type SqliteCommandDefinition =
CommandDefinition <
SQLiteConnection ,
SQLiteCommand ,
SQLiteParameter ,
SQLiteDataReader ,
SQLiteTransaction ,
SqliteDbValue >これは、パラメーターの1つとしてCommandDefinitionを受け入れるSqlCommand関数で後で使用できます。
一般的な制約の観点からさらに別の専門化があります。
/// SQLite Configuration
type SqliteConfiguration =
SqlConfigurationCache <
SQLiteConnection ,
SQLiteCommand >このタイプは、タイプの別のバインダーであり、キャッシュとして機能します。コマンドを実行するときにコマンド定義とともに渡されます。
これはかなり簡単です。あなたがする必要があるのは、次のことだけです。
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 commandDefinition同様に、コマンド実行は同じ原則に従い、別名、関連する強力なパラメーター(現在および特定のADO.NETプロバイダーに対応)をSQLCommand Core関数に渡します。
/// 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 ) commandDefinitionnullヘルパー用の別のモジュールを作成できます。残りは、関連するコア関数に関連するパラメーターを渡すことです。
[<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 ) ここでも同じです。
[<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 bodyそして出来上がり!あなたはすべて解決され、お気に入りのデータベースに対して最もワイルドなコマンドを実行する準備ができています!
TransactionScopeヘルパーこれらのヘルパーは、 TransactionScopeをサポートしている限り、使用しているADO.NETプロバイダーに関係なく動作します。
そうは言っても、私たちはあなたがそれらのヘルパーを使用することを強く落胆させます:
TransactionScope使用することは非常にエラーが発生しやすく、明確なエラーメッセージなしで予期しない動作に遭遇する可能性があります。TransactionScopeを使用するためのアプリケーションが限られていることを考えると、それらのヘルパーは別のライブラリ(IEリポジトリ + Nugetパッケージ)に移動する可能性があります。2PCまたは分散トランザクションの実行可能な回避策が必要な場合は、SAGAパターンなどのアーキテクチャパターンを確認する必要があります。
バグレポート、機能リクエスト、プルリクエストは大歓迎です!
開始するには、貢献ガイドラインをお読みください。
このプロジェクトはMITの下でライセンスされています。
ライセンスの詳細については、ライセンスファイルを参照してください。